Programação Funcional

Propaganda
Programando com Funções: Introdução
Recursão
Notação
Programação Funcional
Carlos Camarão
Universidade Federal de Minas Gerais
DCC-UFMG 2008
www.dcc.ufmg.br/~camarao/cursos/fp
Programação Funcional
Programando com Funções: Introdução
Recursão
Notação
Padrões
Exemplo
Usados para definir funções por meio de equações: permite
determinar construtor usado no argumento e seus componentes. Ex:
length (a:x) = 1 + length x
length []
= 0
Considere: length [1,2]
Notação [1,2] é abreviação (“açúcar sintático”) para 1:2:[]
Chamada envolve casamento de 1:2:[] com a:x — sucesso,
fazendo a denotar 1 e x denotar 2:[]
Chamada recursiva length (2:[]) envolve casamento com a:x
(com sucesso), fazendo agora a denotar 2 e x denotar []
Última chamada length [] faz com que casamento com padrão
a:x falhe: construtor (:) é diferente de []. Casamento com
padrão da próxima equação feito com sucesso
www.dcc.ufmg.br/~camarao/cursos/fp
Programação Funcional
Programando com Funções: Introdução
Recursão
Notação
. . . Padrão
Formado por variáveis e construtores (constantes).
Expressão que não envolve chamada de função
Exemplos:
3
True
x
[a,b]
a:x
PesoArg 10
Construtores (constantes)
não funcionais
Variável
Construtores (constantes)
...
funcionais
Variável casa com qualquer expressão; construtor
(constante) só casa com si próprio; necessário casamento
com componentes, se existirem
www.dcc.ufmg.br/~camarao/cursos/fp
Programação Funcional
Programando com Funções: Introdução
Recursão
Notação
Definindo funções
Exemplos
fac n
odd x
square x
sum_square l
=
=
=
=
product [1..n]
not (even x)
x * x
sum (map square l)
Usando composição de funções, podemos também definir:
odd
sum_square
= not . even
= sum . map square
www.dcc.ufmg.br/~camarao/cursos/fp
Programação Funcional
Programando com Funções: Introdução
Recursão
Notação
. . . Definições de funções
Exemplos
Funções com mais de um parâmetro (funções que retornam
funções):
combinations n k = fac n ‘div ‘ (fac k * fac (n-k ))
raizes a b c = [(-b + sqrt (b*b - 4.0*a*c)) / (2.0*a)
,(-b - sqrt (b*b - 4.0*a*c)) / (2.0*a)
]
www.dcc.ufmg.br/~camarao/cursos/fp
Programação Funcional
Programando com Funções: Introdução
Recursão
Notação
Definição de Função: sintaxe
1
Nome
2
Padrões — um para cada parâmetro
3
Símbolo =
4
Expressão (corpo) que define o resultado
Erro comum: confundir operador de comparação de igualdade
== com =:
negative x = x < 0
positive x = x > 0
isnull x
= x == 0
www.dcc.ufmg.br/~camarao/cursos/fp
Programação Funcional
Programando com Funções: Introdução
Recursão
Notação
Definições usando Padrões
Operador &&, definido no Prelude, pode ser definido do seguinte
modo (usando padrões):
False
False
True
True
&&
&&
&&
&&
False
True
False
True
=
=
=
=
False
False
False
True
Definição seguinte semanticamente equivalente mas mais concisa:
True && True = True
_
&& _
= False
Definição seguinte similar — mas não semanticamente equivalente,
e mais eficiente:
True && b = b
False && _ = False
www.dcc.ufmg.br/~camarao/cursos/fp
Programação Funcional
Programando com Funções: Introdução
Recursão
Notação
Casamento de Padrões
Exemplos
Realizado segundo ordem das equações na definição da
função. Por exemplo, toda chamada à seguinte função
retorna False:
_ && _
= False
True && True = True
Padrão não pode repetir nenhuma variável. Por exemplo, a
seguinte definição é inválida:
b && b = b
_ && _ = False
www.dcc.ufmg.br/~camarao/cursos/fp
Programação Funcional
Programando com Funções: Introdução
Recursão
Notação
Funções “currificadas”
Funções que retornam funções podem ser vistas
informalmente como funções com mais de um argumento.
Exemplo:
add :: Int → (Int → Int)
add x y = x+y
Função add recebe um inteiro x e retorna a função add x; por
sua vez, esta função recebe um inteiro y e retorna x+y .
www.dcc.ufmg.br/~camarao/cursos/fp
Programação Funcional
Programando com Funções: Introdução
Recursão
Notação
Porque funções currificadas são úteis?
Podemos definir novas funções por meio de aplicação parcial:
add :: Int → (Int → Int)
add x y = x+y
inc :: Int → Int
inc = add 1
www.dcc.ufmg.br/~camarao/cursos/fp
Programação Funcional
Programando com Funções: Introdução
Recursão
Notação
Listas, repetição e recursão
Listas são muito usadas em programação funcional.
Em vez de usar comandos de repetição (for, while etc.)
e variáveis cujos valores podem ser modificados por
comandos de atribuição, o modo mais básico de repetição
em Haskell envolve “percorrer” valores contidos em uma
estrutura de dados definida recursivamente, comumente
uma lista, usando uma função recursiva.
O modo não básico envolve uso de funções de ordem
superior, que encapsulam internamente o uso da recursão
www.dcc.ufmg.br/~camarao/cursos/fp
Programação Funcional
Programando com Funções: Introdução
Recursão
Notação
Função de Ordem Superior
Exemplos
Função que recebe ou retorna uma função
map:: (a → b) → [a] → [b]
map f []
= []
map f (a:x) = f a : map f x
>
[
>
[
>
[
map fac [ 1,2,3,4,5 ]
1, 2, 6, 24, 120 ]
map sqrt [ 1.0,2.0,3.0,4.0 ]
1.0, 1.41421, 1.73205, 2.0 ]
map even [ 1..6 ]
False, True, False, True, False, True ]
(.) :: (b → c) → (a → b) → a → c
(f . g) x = f (g x)
www.dcc.ufmg.br/~camarao/cursos/fp
Programação Funcional
Programando com Funções: Introdução
Recursão
Notação
Definições locais
Definições locais podem ocorrer, usando where ou let:
raizes a b c = [ (-b+d) / n, (-b-d) / n ]
where d = sqrt (b*b - 4.0*a*c)
n = 2.0*a
raizes a b c = let d = sqrt (b*b - 4.0*a*c)
n = 2.0*a
in [ (-b+d) / n, (-b-d) / n ]
www.dcc.ufmg.br/~camarao/cursos/fp
Programação Funcional
Programando com Funções: Introdução
Recursão
Notação
Definições com guardas
Definição de função pode utilizar equações com guardas
Exemplo:
signum x | x > 0 = 1
| x == 0 = 0
| x < 0 = -1
Definição equivalente a:
signum x = if x>0 then 1
else if x==0 then 0
else -1
www.dcc.ufmg.br/~camarao/cursos/fp
Programação Funcional
Programando com Funções: Introdução
Recursão
Notação
Aplicação de Funções
associatividade à esquerda
operação de maior precedência
Exemplo:
f x y z - 1 + g 4
equivalente a:
(((f x) y ) z) - 1 + (g 4)
www.dcc.ufmg.br/~camarao/cursos/fp
Programação Funcional
Programando com Funções: Introdução
Recursão
Notação
Estratégia de avaliação preguiçosa
Haskell usa uma estratégia preguiçosa (em inglês, lazy )
de avaliação de expressões: expressão é avaliada se e
somente se o seu resultado é necessário na execução do
programa
Resultado é necessário tipicamente para casamento de
padrão
Exemplo:
repeat x = x : repeat x
one
= head (repeat 1)
Se a computação de one é necessária — para casamento
de padrão como, por exemplo, em print one — a avaliação
da expressão
head (repeat 1)
Programação Funcional
éwww.dcc.ufmg.br/~camarao/cursos/fp
feita apenas para fornecer o
resultado 1:
Programando com Funções: Introdução
Recursão
Notação
Vantagens da avaliação preguiçosa em relação à
gulosa: primeiras observações
Permite definir na própria linguagem funções úteis e
comumente usadas — como &&, || e if-then-else
Permite definição e uso de valores “ilimitados” — como por
exemplo repeat 1 e [2..])
mas: análise de complexidade de tempo e espaço é mais
complexa
www.dcc.ufmg.br/~camarao/cursos/fp
Programação Funcional
Programando com Funções: Introdução
Recursão
Notação
Definições Recursivas
Funções podem ser definidas recursivamente —
indutivamente, se existir caso não recursivo e caso recursivo
sobre argumento “menor”
Exemplo:
fat 0 = 1
fat n = n * fat (n-1)
www.dcc.ufmg.br/~camarao/cursos/fp
Programação Funcional
Programando com Funções: Introdução
Recursão
Notação
Exercício
Haskell provê uma função lines:: String → [String] para
separação de um texto em linhas, omitindo os caracteres de
separação de linhas (’\n’).
Por exemplo:
> lines "lin1\nlin2"
["lin1","lin2"]
> lines "a*b\n\nxx!"
["a*b","","xx!"]
lines considera \n como separador de linhas. No Windows, a
separação de linhas, em arquivos que armazenam textos
(seqüências de caracteres separadas em linhas), é indicada
por dois caracteres: \r\n. Ao usarmos um arquivo-texto
Windows em um programa Unix, o caractere \r será gravado
no final de cada linha.
www.dcc.ufmg.br/~camarao/cursos/fp
Programação Funcional
Programando com Funções: Introdução
Recursão
Notação
Regra de Leiaute
Haskell usa uma “regra de leiaute” para reduzir uso de
símbolos delimitadores e permitir o uso de espaços para
expressar aninhamento de definições em programas.
raizes a b c = [ (-b+d) / n, (-b-d) / n ]
where d = sqrt (b*b - 4.0*a*c)
n = 2.0*a
A posição (coluna) c de início da primeira definição em um
bloco b determina que:
outras definições em b bloco devem começar na mesma
posição c;
início de definição em posição maior (menor) que c indica
aninhamento mais interno (externo) em relação a b.
www.dcc.ufmg.br/~camarao/cursos/fp
Programação Funcional
Programando com Funções: Introdução
Recursão
Notação
. . . Regra de leiaute
Opcional
Estrutura de blocos pode ser expressa explicitamente, sem uso
da regra de leiaute, por meio de delimitadores: abre-chaves
para iniciar e fecha-chaves para finalizar blocos, e ; para
separar definições em um bloco.
f x y = g (x + w)
where g u = u + v
where v = u * u
w = 2 + y
Expressa a mesma função (embora de modo mais legível) que:
f x y = g (x + w)
where { g u = u + v where { v = u * u};
w = 2 + y }
www.dcc.ufmg.br/~camarao/cursos/fp
Programação Funcional
Programando com Funções: Introdução
Recursão
Notação
Comentários
Comentários podem ser introduzidos de duas formas:
dois hífens - iniciam um comentário que vai até o fim da
linha
os caracteres {- iniciam e os caracteres -} terminam um
comentário
www.dcc.ufmg.br/~camarao/cursos/fp
Programação Funcional
Download