Escolar Documentos
Profissional Documentos
Cultura Documentos
1. Introdução
1.1. Histórico
3. M pode, na prática ou pelo menos em princípio, ser executado por um ser humano sem
nenhuma outra ajuda que um lápis e papel;
4. M não exige do ser humano que o executa nenhum brilhantismo, originalidade ou genialidade.
No fundo, isto nada mais é que adefinição matemática formal do conceito de algoritmo, realizada
primeiramente por Alonzo Church através da utilização de seu enfoque.
Detalhes sobre podem ser encontrados na Stanford Encyclopedia of Philosophy.
A conseqüência direta disso para nós mortais comuns, além de progressos na Teoria da Computação
dos quais usufruímos, é a de que o Cálculo Lambda serviu de base teórica para as Lingugens de
Programação Funcionais.
1.2. Linguagens Funcionais
• Interessantes pela sua simplicidade sintática
• Facilidade de descrever-se problemas recursivos.
• Muitas das implementações são poucos aceitas devido à ineficiência em comparação com
linguagens de programação „tradicionais“.
• Novas implementações de interpretadores/compiladores e novas linguagens mais modernas
tem surgido.
Exemplos:
• LISP (LISt Processing - década de 60). Muito simples em muitos aspectos. Atualmente é
ainda a mais utilizada.
• Miranda (Turner 1985)
• Haskell (1990)
• Orwell (Wadler 1985)
• Outras: ML, KRC, LML, SASL.
Todas as aplicações de funções no Cálculo Lambda são escritas em notação prefixada. Assim a
função + precede os argumentos 4 e 5. Um exemplo um pouco mais complexo é:
(+ (* 5 6) (* 8 3))
Do ponto de vista de implementação, um programa funcional pode ser visto como uma expressão,
que é “executada“ através da sua avaliação.
A avaliação ocorre através da seleção repetida de uma expressão redutível (redex) e de sua
redução. Expressão redutível é aquela que pode ser avaliada imediatamente.
• No exemplo temos dois redexes: (* 5 6) e (* 8 3).
• A expressão inteira (+ (* 5 6) (* 8 3)) não é um redex, uma vez que a função +
necessita de ser aplicada a dois números para poder ser redutível.
• Através da escolha arbitrária de do primeiro redex para redução, podemos escrever:
(+ (* 5 6) (* 8 3)) -> (+ 30 (* 8 3))
onde -> é pronunciado „reduz para“.
• Agora há somente um redex (* 8 3), do qual resulta: (+ 30 24)
• Esta redução gera um novo redex, que reduz: (+ 30 24) -> 54
executa.
Isto pode ser evitado através do estabelecimento de convenções sobre a precedência de operadores
e de funções (exemplo: multiplicação liga de forma mais forte do que adição). As vezes, parênteses
não podem ser omitidos, como: (b + c)/a
Convenções similares são úteis no Cálculo Lambda.
Considere:
((+ 3) 2)
• Através do estabelecimento da convenção de que aplicação de funções associa à esquerda,
podemos escrever:
(+ 3 2)
ou até:
+32
Abreviações deste tipo foram feitas no exemplos anteriores.
• Um exemplo mais complexo é a expressão:
((f ((+ 4) 3)) (g x))
que pode ser escrita:
f (+ 4 3)
IF FALSE E E -> E
t f f
O exemplo acima pode ser lido: „Aquela (λ) função de (x) a qual (.) adiciona x a 1 (+ x 1).
Uma abstração lambda sempre consiste dessas partes: o λ , o parâmetro formal, o . e o corpo. Uma
abstração lambda pode ser considerada similar a uma definição de função em uma linguagem de
programação convencional, como „C“:
inc ( x ) {
int x;
( return( x + 1); }
• O parâmetro formal da abstração lambda corresponde ao parâmetro formal da função.
• O corpo da abstração é antes uma expressão do que uma sequência de comandos.
Diferença: funções em linguagens convencionais necessitam de um nome como inc, enquanto
que em Cálculo Lambda funções são anônimas.
Sintaxe: O corpo de uma abstração se extende o tanto para a direita quanto for possível.
• Assim, na expressão:
(λ x . + x 1) 4
(λ x . (+ x 1)) 4.
λ x . + x 1
2.1.5. Sumário
λ
Definimos uma expressão lambda (expressão- ) como uma expressão no cálculo lambda.
A sintaxe de uma expressão lambda em notação BNF é dada abaixo.
<exp> ::= <constante> constantes embutidas
| <variável> nomes de variáveis
| <exp> <exp> aplicações
|
λ <variável> . <exp> abstrações lambda
ou, de forma mais estruturada (G.Revesz):
<exp>::= <constante>|<variável>|<aplicação>|<abstração>
<aplicação> ::= (<exp>) <exp>
<abstração> ::=
λ <variável> . <exp>
Esta sintaxe então nos permite formar expressões-λ como:
λ x.x λ x.λ y.(y)x λ x.(f)x
(f)3
λ f.(f)2 λ λ
( y.(x)y) x.(u)x
Resumindo a nomenclatura
Uma abstração-
λ é formada com o símbolo especial
λ seguido por uma variável, seguida por um
ponto, seguido por uma expressão-
λ arbitrária.
• O propósito da operação de abstração é o de formar uma expressão unária a partir de uma
expressão-
λ dada. A variável que ocorre próxima ao λ inicial dá nome ao argumento.
• Funções com mais de um argumento são formadas por abstrações repetidas.
Até agora foi descrita a sintaxe do cálculo- λ . Para chamá-lo de “cálculo”, devemos porém dizer
como “calcular” com ele.
Basicamente isto é realizado através de três regras de conversão, que descrevem como conver-
ter uma expressão- λ em outra.
Consideremos a expressão- λ : ( λ x. + x y) 4
A razão é que x ocorre atado pelo λ x, é somente um encaixe dentro do qual o argumento 4 é
colocado quando a abstração- λ for aplicada ao argumento.
Por outr lado, y não é atado por nenhum λ e assim ocorre livre na expressão.
A ocorrência de uma variável é atada se há uma expressão- λ envolvente que a amarra, senão
é livre. No exemplo a seguir, x e y ocorrem atados, z porém, ocorre livre:
λ x. + (( λ y. + y z) 7) x
Observe que os termos atado e livre se referem a ocorrências específicas da variável em uma
expressão.
Uma variável pode possuir tanto uma ocorrência atada como uma livre em uma expressão.
Considere o exemplo:
+ x (( λ x. + x 1) 4)
Aqui x ocorre livre (a primeira vez) e atada (a segunda). Cada ocorrência individual de uma
variável deve ser ou atada ou livre.
Nota: nenhuma variável oorre atada em uma expressão consistindo e uma única constante.
Definição de ocorre atada:
2.2.2. Conversão-Beta
Uma abstração- λ denota uma função. Assim necessitamos descrever como aplicá-la a um
argumento.
( λ x. + x 1) 4 → +41
( λ x. + x x) → +55
→ 10
Nesse caso não há ocorrências do parâmetro formal (x), pelo qual o argumento 5 poderia ser
substituído. Assim o argumento é descartado sem uso.
( λ x. ( λ y. - y x)) 4 5 → ( λ y. - y 4) 5
→ -54
→ 1
Aqui se observa também o efeito do Currying: a aplicação da abstração λ x retornou uma fun-
ção (a abstração λ y) como seu resultado. Esta por sua vez, aplicada resultou em ( - 5 4).
Funções encapsuladas umas dentro das outras são comumente abreviadas:
( λ x. ( λ y. E)) para ( λ x. λ y. E)
( λ f. f 3)( λ x. + x 1) → ( λ x. + x 1) 3
→ +31
→ 4
Uma instância da abstração λx é substituída por f onde quer que f apareça no corpo da
abstração de λ f.
Nomenclatura:
( λ x. + (- x 1)) x 3
Outro exemplo:
CONS = ( λ a. λ b. λ f.f a b)
HEAD = ( λ c.c ( λ a. λ b.a))
TAIL = ( λ c.c ( λ a. λ b.b))
as fórmulas acima obedecem às regras para CONS, HEAD e TAIL definidas anteriormente
(Seção 2.1.3). Exemplo:
Isto significa:
• Não existe necessidade real para as funções embutidas HEAD e CONS ou TAIL.
• Todas as funções embutidas podem ser modeladas como abstrações-lambda.
• De um ponto de vista teórico, isto é satisfatório, para efeitos práticos porém, não é utilizado
(eficiência).
2.2.3. Conversão-Alfa
Considere:
( λ x. + x 1) e ( λ y. + y 1)
Notação:
( λ x. + x 1) ↔ ( λ y. + y 1)
α
2.2.4. Conversão-Eta
( λ x. + 1 x) ↔ (+ 1)
η
( λ x. F x) ↔ F
η
A condição de que F deve denotar uma função previne outras conversões errôneas envovlendo
constantes embutidas (predefinidas). EXemplo:
• Quando as duas expressões denotam funções, esta prova pode se tornar extremamente
longa.
Há porém, métodos para abreviar provas, que não sacrificam o seu rigor. Exemplo:
IF TRUE (( λ p.p) 3) e ( λ x. 3)
Ambas as expressões denotam a mesma função, que invariavelmente retorna o valor 3, não
importa o valor de seu argumento e seria de se esperar que ambas sejam interconvertíveis.
↔ ( λ x. IF TRUE 3 x)
η
→ ( λ x. 3)
IF TRUE (( λ p.p) 3) w ( λ x. 3) w
→ ( λ p.p) 3 → 3
→ 3
Esta prova tem a vantagem de somente utilizar a redução e de evitar o uso explícito da conver-
são- η .
F1 w → E
F2 w → E
onde w é uma variável que não ocorre livre tampouco em F1 como em F2, e E é alguma expres-
são, então podemos raciocinar da seguinte forma:
F1 ↔ ( λ w. F1 w)
η
↔ ( λ w. E)
↔ ( λ w. F2 w)
↔ F2
η
Não é sempre que expressões- λ que “deveriam” dizer a mesma coisa são interconvertíveis.
Isto será tratado mais tarde.
Dentro deste contexto, podemos considerar as funções embutidas (predefinidas) como mais
uma forma de conversão. Esta forma de conversão recebe o nome de conversão- δ .
Poderá uma seqüência de redução diferente levar a uma forma normal diferente?
Analisar uma expressão para encontrar o redex ótimo para ser reduzido é
aparentemente muito mais trabalhoso e gasta mais tempo do que arriscar uma redução
com mais passos seguindo "cegamente" a ordem normal.
² Alguns autores como (Levy, J.J.; Optimal Reductions in the Lambda Calculus in Essays
on Combinatory Logic, Academic Press, 1980) se ocuparam de algoritmos para
encontrar ordens ótimas ou quase-ótimas de redução que preservassem as qualidades
da ordem normal de redução. Isto é assunto de um tópico avançado que não cabe nesta
disciplina.
Para resolver este problema iniciamos com um caso onde encontramos a recursão na sua
forma mais pura:
FAC = (λn. ... FAC ... )
² Aplicando uma abstração-b sobre FAC, podemos transformar esta definição em:
FAC = (λfac.(λn. (...fac...)))FAC
² Esta definição podemos escrever da forma:
FAC = H FAC (2.1)
² onde:
H = (λfac.(λn. (...fac...)))
² A definição de H é trivial.
É uma abstração lambda ordinária e não usa recursão.
² A recursão é expressa somente pela definição 2.1.
A definição 2.1 pode ser encarada mais como uma equação matemática. Por exemplo, para
resolver a equação matemática
x2 - 2 = x
nós procuramos por valores de x que satisfazem a equação (neste caso x = -1 e x = 2) .
De forma similar, para resolver 2.1, nós procuramos uma expressão lambda para FAC que
satisfaça 2.1.
A equação 2.1 FAC = H FAC postula que, quando a função H é aplicada a FAC, o resultado
é FAC.
Nós dizemos que FAC é um ponto fixo de H.
Uma função pode ter mais de um ponto fixo. Por exemplo, na função abaixo, tanto 0 quanto
1 são pontos fixos da função:
λx. * x x
a qual calcula o quadrado de seu argumento.
Em resumo, estamos procurando por um ponto fixo para H. Claramente isto depende
somente de H.
Para isto, invente-se, a título provisório, uma função Y, a qual toma uma função como
argumento e devolve o seu ponto fixo como resultado.
Assim Y tem o comportamento de:
Y H = H (Y H)
e Y é chamado de combinador de ponto fixo.
Assim, caso possamos produzir um Y, temos uma solução para o problema da recursividade.
Para 2.1 podemos prover a seguinte solução:
FAC = Y H
a qual é uma definição não-recursiva de FAC.
Como teste para esta estratégia, podemos computar o valor de (FAC 1). Tomemos as
definições de FAC e H:
FAC = Y H
H = (λfac.λn. IF (= n 0) 1 (* n (fac (- n 1))))
Assim: FAC 1
-> Y H 1
-> H (Y H) 1
->(lfac.ln.IF (= n 0) 1 (* n (fac (- n 1))))(Y H) 1
->(ln.IF (= n 0) 1 (* n (Y H (- n 1)))) 1
-> IF (= 1 0) 1 (* 1 (Y H (- 1 1)))
-> * 1 (Y H 0)
= * 1 (H (Y H) 0)
= * 1 ((lfac.ln.IF (= n 0) 1 (* n (fac (- n 1))))(Y H) 0)
-> * 1 ((ln.IF (= n 0) 1 (* n (Y H(- n 1)))) 0)
II. Recursividade:
1. Dê uma definição recursiva para o maior divisor comum de dois inteiros e calcule o valor
de ((mdc) 10) 14) utilizando o combinador Y.
2. Tente o mesmo para os números de Fibonacci e use Y para computar (Fibo) 5.
1. Redução direta:
(λx.x(xy))N-> N(Ny)
3. Menos trivial:
(λx.(λy.xy)N)M -> (λy.My)N
pois x está livre em (λy.xy)N
-> MN
5. Exemplo catastrofal:
(λx.xxy)(λx.xxy)-> (λx.xxy)(λx.xxy)y
-> (λx.xxy)(λx.xxy)yy
-> (λx.xxy)(λx.xxy)yyy
-> (λx.xxy)(λx.xxy)yyyy
definindo a série:
1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233..
para os números:
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12..
e o algoritmo é bem simples:
fibonacci ( n )
(
se n <= 1
retorne 1
senão
retorne ( fibonacci (n-1) + fibonacci (n-2) )
)
Assim: Eval[[ x ]] ρ = ρ x
( Eval[[ λx.E ]] ρ) a
Resumindo:
• O valor de uma abstração lambda, aplicada a um argumento, é o
valor do corpo da abstração, em um contexto onde o parâmetro
formal está atado ao argumento.
• Formalmente: Eval[[ λx.E ]] ρ a = Eval[[ E ]] ρ [x=a]
onde a notação x=a significa “a função ρ extendida com a infor-
mação de que a variável x está atada ao valor a”.
• De forma mais precisa:
ρ [x=a]x = a
ρ [x=a]y = ρ y
Resumo geral:
Eval[[ k ]] ρ = vide seção 2.5.3
Eval[[ x ]] ρ =ρx
Eval[[ E1 E2 ]] ρ = (Eval[[ E1 ]] ρ) (Eval[[ E2 ]] ρ)
Eval[[ λx.E ]] ρ a = Eval[[ E ]] ρ [x=a]
onde:
k é uma constante ou função embutida,
x é uma variável,
E, E1, E2 são expressões-lambda.
Portanto:
Eval[[ E1 ]] ρ = Eval[[ E2 ]] ρ
Eval[[ E1 ]] = Eval[[ E2 ]]
2.5.2. O Símbolo 8
2.5.4. Estriticidade
g aVc=V
2.5.5. Fim:
2.5.6. LISP:
• Início próxima aula. Uma sugestão para leitura é:
Oakey, Steve; LISP para Micros. Editora Campus, 1986
ISBN 85-7001-326-4