1 / 30

Programação Funcional

Programação Funcional. Generalização em Haskell. 7a. Seção de Slides. Hoje. Funções de mais alta ordem ou funções de ordem superior (“higher order function”) Generalizações de funções Polimorfismos. Funções como Argumentos.

Download Presentation

Programação Funcional

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Programação Funcional Generalização em Haskell 7a. Seção de Slides

  2. Hoje • Funções de mais alta ordem ou funções de ordem superior (“higher order function”) • Generalizações de funções • Polimorfismos

  3. Funções como Argumentos • Assim como números, tuplas e listas, também funções (já visto nas listas de exercício) podem ser argumentos para outras funções. • Funções que recebem outras funções como argumentos são chamadas de: funções de mais alta ordem ou funções de ordem superior (“higher order function”)

  4. 1. 2 . 3 . double :: [Int] -> [Int] double [] = [] double (a:x) = (2*a) : double x 1. 2 . 3 . treble :: [Int] -> [Int] treble [] = [] treble (a:x) = (3*a) : treble x Motivação • Função para retornar uma lista com os elementos “vezes dois” : • Função para retornar uma lista com os elementos "triplicados":

  5. 1. 2 . 3 . double :: [Int] -> [Int] double [] = [] double (a:x) = (2*a) : double x 1. 2 . 3 . treble :: [Int] -> [Int] treble [] = [] treble (a:x) = (3*a) : treble x Motivação • O padrão (formato) de ambas as definições, é o mesmo! • A única coisa que muda é o modo de como os elementos da lista são transformados (2*a ora 3*a).

  6. 1. 2 . 3 . double :: [Int] -> [Int] double lista = [2*x | x <- lista] double [4, 8 .. 31] [8,16,24,32,40,48,56] 1. 2 . 3 . treble :: [Int] -> [Int] treble lista = [3*x | x <- lista] treble [4, 8 .. 31] [12,24,36,48,60,72,84] Relembrando A proposta é: “seria possível termos funções que generalizassem estas definições sobre parâmetros?”

  7. 1. 2 . 3 . times2, times3 :: Int -> Int times2 n = 2*n times3 n = 3*n Generalizando • Uma função genérica para transformar listas de inteiros deveria então ter dois argumentos: • uma função que especifica a transformação a ser realizada sobre cada elemento da lista; • a lista a ser transformada. • Exemplos de funções de transformação para elementos (inteiros)... • Do exemplo anterior teríamos que as funções de transformação são do tipo:

  8. 1. 2 . mapInt f [] = [] mapInt f (a:x) = (f a) : mapInt f x Generalizando • Função genérica para transformar listas de inteiros: • A função mapInt recebe como argumentos: • Uma função f que especifica a transformação a ser realizada em cada elemento da lista; • E a lista a ser transformada. Mantenha em mente estas construções ..

  9. 1. 2 . mapInt f [] = [] mapInt f (a:x) = (f a) : mapInt f x 1. mapInt f lista = [ (f a) | a <- lista] Generalizando: outra solução • Função genérica para transformar listas de inteiros: • Outra solução - usando “list comprehensions”:

  10. 1. 2 . 3 . 4 . mapInt f [] = [] mapInt f (a:x) = (f a) : mapInt f x times2 n = 2*n Exemplo mapInt times2 [1,4] = (times2 1) : mapInt times2 [4] = 2 : mapInt times2 [4] = 2 : ((times2 4) : mapInt times2 []) = 2 : (8 : mapInt times2 []) = 2 : (8 : []) = [2,8] Ativando...

  11. 1. 2 . 3 . 4 . mapInt f [] = [] mapInt f (a:x) = (f a) : mapInt f x times3 n = 3*n Exemplo MapInt times3 [1,4] = (times3 1) : mapInt times3 [4] = 3 : mapInt times3 [4] = 3 : ((times3 4) : mapInt times3 []) = 3 : (12 : mapInt times3 []) = 3 : (12 : []) = [3,12] Ativando...

  12. Main> double [1,4] [2,8] Main> treble [1,4] [3,12] 1. 2 . 3 . 4 . 5 . 6 . 7 . 8 . 9 . mapInt f [] = [] mapInt f (a:x) = (f a) : mapInt f x times2, times3 :: Int -> Int times2 n = 2*n times3 n = 3*n Redefinindo double e treble double lista = mapInt times2 lista treble lista = mapInt times3 lista

  13. 1. 2 . 3 . mapInt :: t1??? -> t2??? -> t3??? mapInt f [] = [] mapInt f (a:x) = (f a) : mapInt f x Definindo tipos • Qual o tipo de mapInt ? • o primeiro argumento é uma função que transforma um inteiro em outro inteiro: (2*Int ou 3*Int, resultando em Int, logo: Int -> Int) • o segundo argumento é uma lista de inteiros: [Int]; • retorna outra lista de inteiros : [Int].

  14. 1. 2 . 3 . mapInt :: (Int->Int) -> [Int] -> [Int] mapInt f [] = [] mapInt f (a:x) = (f a) : mapInt f x Definindo tipos • Repetindo: • o primeiro argumento é uma função que transforma um inteiro em outro inteiro; • o segundo argumento é uma lista de inteiros; • retorna outra lista de inteiros.

  15. Vantagens • Ao usar funções de mais alta ordem, consegue-se mais clareza e simplicidade nas definições. • Fica mais fácil proceder modificações. Por exemplo, se for necessário modificar apenas a função de transformação de elementos. • Permite reutilização (reusabilidade de código) de definições. A função mapInt pode ser usada em muitas outras situações.

  16. 1 sales :: Int -> Int 2 sales x 3 | x == 0 = 12 4 | x == 1 = 20 5 | x == 2 = 18 6 | x == 3 = 25 7 | otherwise = 0 8 totalSales :: Int -> Int 9 totalSales n 10 | n == 0 = sales 0 11 | otherwise = totalSales (n-1) + (sales n) Exemplo 1 Função totalSales usa uma definição específica para sales.

  17. 8. 9 . 10. 11. 12. 13. total :: (Int - > Int) -> Int -> Int total f n | n==0 = f 0 | otherwise = total f (n-1) + f n Exemplo 1 - generalizando • Contudo, “a genérica”, função total, pode ser usada em muitas outras situações... totalSales n = total sales n

  18. 8. 9 . 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. total :: (Int - > Int) -> Int -> Int total f n | n==0 = f 0 | otherwise = total f (n-1) + f n totalSales n = total sales n sumSquares :: Int -> Int sumSquares n = total sq n sq :: Int -> Int sq x = x*x Exemplo 1 - generalizando Calcula a soma dos quadrados de 0 a n. É o reuso de código !

  19. Exemplo 2 • Soma dos elementos de uma lista de inteiros: e1 + e2 + ... + ek • Máximo elemento de uma lista de inteiros: e1 'maxi' e2 'maxi' ... 'maxi' ek • Tipos das funções aplicadas a pares de elementos: (+), 'maxi' :: Int -> Int -> Int

  20. 1. 2 . 3 . 4 . 5 . 6 . 7 . 8 9 10 11 12 . foldInt :: (Int -> Int -> Int) -> [Int] -> Int foldInt f [a] = a foldInt f (a:b:x) = f a (foldInt f (b:x)) ou foldInt f (a:x) = f a (foldInt f x) sumList lista = foldInt (+) lista maxList lista = folInt maxi lista Exemplo 2 - generalizando

  21. 1. 2 . 3 . 4 . 5 . 6 . 7 . 8 9 10 11 12 . Detalhando maior_da_LISTA lista = reduz maior_de_2 lista maior_de_2 x y | x >= y = x | otherwise = y reduz f [a] = a reduz f (a : resto) = f a (reduz f resto) maior_da_LISTA [8,1,7,-3,9,-7] 9

  22. Exemplo 3 • Filtrar algarismos/números de um texto: digits "29 February 1996" = "291996" • Filtrar letras de um texto: letters "29 February 1996" = "February" • Tipos das funções aplicadas a cada elemento: isDigit, isLetter :: Char -> Bool

  23. 1. 2 . 3 . 4 . 5 . 6 . 7 . 8 . 9 . 10. 11. 12. 13. 14. filterString :: (Char -> Bool) -> [Char] -> [Char] filterString p [] = [] filterString p (a:x) | p a = a : filterString p x | otherwise = filterString p x isDigit, isLetter :: Char -> Bool isDigit ch = ('0'<=ch && ch<='9') isLetter ch = ('a'<=ch && ch <='z') || ('A'<=ch && ch <='Z') digits st = filterString isDigit st letters st = filterString isLetter st Exemplo 3 - generalizando

  24. 1. 2 . 3 . 4 . 5 . 6 . 7 . 8 . 9 . 10. 11. filterString :: (Char -> Bool) -> [Char] -> [Char] filterString p x = [ a | a <- x , p a ] isDigit, isLetter :: Char -> Bool isDigit ch = ('0'<=ch && ch<='9') isLetter ch = ('a'<=ch && ch <='z') || ('A'<=ch && ch <='Z') digits st = filterString isDigit st letters st = filterString isLetter st Exemplo 3 - outra solução

  25. 1. 2 . length [] = 0 length (a:x) = 1 + length x Polimorfismo • Função que calcula o comprimento de uma lista: • Qual o tipo de length ? length :: [t] -> Int t = variável de tipo

  26. 1. 2 . Listas> comp_lista [1 .. 20] 20 Listas> comp_lista ['a' .. 'z'] 26 Listas> comp_lista ["a" .. "z"] ERROR - Illegal Haskell 98 class constraint in inferred type *** Expression : comp_lista (enumFromTo "a" "z") *** Type : Enum [Char] => Integer Listas> comp_lista ["a" , "c", "z"] 3 Listas> comp_lista " xxxxxxx " 9 Polimorfismo - Exemplo comp_lista [] = 0 comp_lista (_:resto) = 1 + comp_lista resto

  27. 1. 2 . rev [] = [] rev (a:x) = rev x ++ [a] Outros exemplos de polimorfismo • Função que calcula o reverso de uma lista: • Qual o tipo de rev ? rev :: [t] -> [t] Importante: retorna lista do mesmo tipo que o argumento de entrada

  28. 1. 2 . zip (a:x) (b:y) = (a,b) : zip x y zip _ _ = [] Outros exemplos de polimorfismo • Função para construir lista de pares: • Qual o tipo de zip ? zip :: [t] -> [u] -> [ (t,u) ] Importante: os tipos t e u não estão relacionados!

  29. Vantagens • Ao usar polimorfismo, as definições ficam mais genéricas; • As definições podem ser reutilizadas em muitas situações diferentes; • Por exemplo, a função length calcula o comprimento de listas de qualquer tipo; • Sem polimorfismo, seria necessário criar uma versão de length para cada tipo de lista a ser utilizada.

  30. Conclusões • Funções de ordem superior exigem abstração, contudo: elegantes! • Temos 3 clássicas: reduzir(seleciona um da lista)filtrar(seleciona nenhum, alguns ou todos), e mapear(sobre todos); • Ao usar polimorfismo, as definições das funções ficam mais genéricas: seria necessário criar uma versão de comp_lista para cada tipo de lista a ser utilizada.

More Related