Upload
tierney-gaughan
View
27
Download
0
Embed Size (px)
DESCRIPTION
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. - PowerPoint PPT Presentation
Citation preview
Programação Programação FuncionalFuncional
7a. Seção de Slides
Generalização em HaskellGeneralização em Haskell
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
Motivação
1 .2 .3 .
1 .2 .3 .
double :: [Int] -> [Int]double [] = []double (a:x) = (2*a) : double x
• Função para retornar uma lista com os elementos “vezes dois” :
1 .2 .3 .
1 .2 .3 .
treble :: [Int] -> [Int]treble [] = []treble (a:x) = (3*a) : treble x
• Função para retornar uma lista com os elementos "triplicados":
5
Motivação
1 .2 .3 .
1 .2 .3 .
double :: [Int] -> [Int]double [] = []double (a:x) = (2*a) : double x
1 .2 .3 .
1 .2 .3 .
treble :: [Int] -> [Int]treble [] = []treble (a:x) = (3*a) : treble x
• 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
Relembrando
1 .2 .3 .
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 .
1 .2 .3 .
treble :: [Int] -> [Int]treble lista = [3*x | x <- lista]treble [4, 8 .. 31][12,24,36,48,60,72,84]
A proposta é: “seria possível termos funções que generalizassem estas definições sobre parâmetros?”
7
Generalizando
1 .2 .3 .
1 .2 .3 .
times2, times3 :: Int -> Inttimes2 n = 2*ntimes3 n = 3*n
• 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
Generalizando
1 .2 .
1 .2 .
mapInt f [] = []mapInt f (a:x) = (f a) : mapInt f x
• 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 menteestas construções ..
9
Generalizando: outra solução
1 .2 .
1 .2 .
mapInt f [] = []mapInt f (a:x) = (f a) : mapInt f x
• Função genérica para transformar listas de inteiros:
• Outra solução - usando “list comprehensions”:
1 .1 .mapInt f lista = [ (f a) | a <- lista]
10
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]
1 .2 .3 .4 .
1 .2 .3 .4 .
mapInt f [] = []mapInt f (a:x) = (f a) : mapInt f x
times2 n = 2*n
Ativando...
11
Exemplo
1 .2 .3 .4 .
1 .2 .3 .4 .
mapInt f [] = []mapInt f (a:x) = (f a) : mapInt f x
times3 n = 3*n
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
Redefinindo double e treble
Main> double [1,4][2,8]Main> treble [1,4][3,12]
Main> double [1,4][2,8]Main> treble [1,4][3,12]
1 .2 .3 .4 .5 .6 .7 .8 .9 .
1 .2 .3 .4 .5 .6 .7 .8 .9 .
mapInt f [] = []mapInt f (a:x) = (f a) : mapInt f x
times2, times3 :: Int -> Inttimes2 n = 2*ntimes3 n = 3*n
double lista = mapInt times2 listatreble lista = mapInt times3 lista
13
Definindo tipos
1 .2 .3 .
1 .2 .3 .
mapInt :: t1??? -> t2??? -> t3???mapInt f [] = []mapInt f (a:x) = (f a) : mapInt f x
• 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
Definindo tipos
1 .2 .3 .
1 .2 .3 .
mapInt :: (Int->Int) -> [Int] -> [Int]mapInt f [] = []mapInt f (a:x) = (f a) : mapInt f x
• 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
Exemplo 1
1 sales :: Int -> Int2 sales x3 | x == 0 = 124 | x == 1 = 205 | x == 2 = 186 | x == 3 = 257 | otherwise = 0
1 sales :: Int -> Int2 sales x3 | x == 0 = 124 | x == 1 = 205 | x == 2 = 186 | x == 3 = 257 | otherwise = 0
8 totalSales :: Int -> Int9 totalSales n10 | n == 0 = sales 011 | otherwise = totalSales (n-1) + (sales n)
8 totalSales :: Int -> Int9 totalSales n10 | n == 0 = sales 011 | otherwise = totalSales (n-1) + (sales n)
Função totalSales usauma definição
específica para sales.
Função totalSales usauma definição
específica para sales.
17
Exemplo 1 - generalizando
8 .9 .10.11.12.13.
8 .9 .10.11.12.13.
total :: (Int - > Int) -> Int -> Inttotal f n | n==0 = f 0 | otherwise = total f (n-1) + f n
• Contudo, “a genérica”, função total, pode ser usada em muitas outras situações...
totalSales n = total sales n
18
Exemplo 1 - generalizando
8 .9 .10.11.12.13.14.15.16.17.18.19.
8 .9 .10.11.12.13.14.15.16.17.18.19.
total :: (Int - > Int) -> Int -> Inttotal f n | n==0 = f 0 | otherwise = total f (n-1) + f n
totalSales n = total sales n
sumSquares :: Int -> IntsumSquares n = total sq n
sq :: Int -> Intsq x = x*x
Calcula a somados quadrados
de 0 a n.
Calcula a somados 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
Exemplo 2 - generalizando
1 .2 .3 .4 .5 .6 .7 .89101112 .
1 .2 .3 .4 .5 .6 .7 .89101112 .
foldInt :: (Int -> Int -> Int) -> [Int] -> Int
foldInt f [a] = afoldInt f (a:b:x) = f a (foldInt f (b:x))oufoldInt f (a:x) = f a (foldInt f x)
sumList lista = foldInt (+) listamaxList lista = folInt maxi lista
21
Detalhando
1 .2 .3 .4 .5 .6 .7 .89101112 .
1 .2 .3 .4 .5 .6 .7 .89101112 .
maior_da_LISTA [8,1,7,-3,9,-7]
9
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)
22
Exemplo 3
• Filtrar algarismos/números de um texto:digits "29 February 1996"= "291996"
• Filtrar letras de um texto:
• Tipos das funções aplicadas a cada elemento:
isDigit, isLetter :: Char -> Bool
letters "29 February 1996"= "February"
23
Exemplo 3 - generalizando
1 .2 .3 .4 .5 .6 .7 .8 .9 .10.11.12.13.14.
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 -> BoolisDigit ch = ('0'<=ch && ch<='9')isLetter ch = ('a'<=ch && ch <='z') || ('A'<=ch && ch <='Z')
digits st = filterString isDigit stletters st = filterString isLetter st
24
Exemplo 3 - outra solução
1 .2 .3 .4 .5 .6 .7 .8 .9 .10.11.
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 -> BoolisDigit ch = ('0'<=ch && ch<='9')isLetter ch = ('a'<=ch && ch <='z') || ('A'<=ch && ch <='Z')
digits st = filterString isDigit stletters st = filterString isLetter st
25
Polimorfismo
• Função que calcula o comprimento de uma lista:
• Qual o tipo de length ?
length :: [t] -> Int
1 .2 .
1 .2 .
length [] = 0length (a:x) = 1 + length x
t = variável de tipot = variável de tipo
26
Listas> comp_lista [1 .. 20]20Listas> comp_lista ['a' .. 'z']26Listas> 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"]3Listas> comp_lista " xxxxxxx "9
Listas> comp_lista [1 .. 20]20Listas> comp_lista ['a' .. 'z']26Listas> 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"]3Listas> comp_lista " xxxxxxx "9
Polimorfismo - Exemplo
1 .2 .
1 .2 .
comp_lista [] = 0comp_lista (_:resto) = 1 + comp_lista resto
27
Outros exemplos de polimorfismo
• Função que calcula o reverso de uma lista:
• Qual o tipo de rev ?
rev :: [t] -> [t]
1 .2 .
1 .2 .
rev [] = []rev (a:x) = rev x ++ [a]
Importante: retorna lista do mesmo tipoque o argumento de entrada
Importante: retorna lista do mesmo tipoque o argumento de entrada
28
Outros exemplos de polimorfismo
• Função para construir lista de pares:
• Qual o tipo de zip ?
zip :: [t] -> [u] -> [ (t,u) ]
1 .2 .
1 .2 .
zip (a:x) (b:y) = (a,b) : zip x yzip _ _ = []
Importante: os tipos t e unão estão relacionados!
Importante: os tipos t e unã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.