Programação Funcional
-
Upload
tierney-gaughan -
Category
Documents
-
view
27 -
download
0
description
Transcript of Programação Funcional
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.