Introducción al Cálculo Lambda

Anuncio
Introducción al Cálculo Lambda
Dr. Alejandro Guerra-Hernández
Departamento de Inteligencia Artificial
Universidad Veracruzana
Facultad de Física e Inteligencia Artificial
Sebastián Camacho No 5, Xalapa, Ver., México 91000
aguerra@uv.mx
www.uv.mx/aguerra
9 de marzo de 2007
¿Puede uno resolver todos los problemas formulados en un lenguaje universal? Esta pregunta, conocida como el Entscheidungproblem, fue abordada de
manera independiente (y resuelta de manera negativa) por Alan Turing y Alonzo Church. Para ello, Turing inventó una clase de máquinas (después llamadas
máquinas de Turing) sobre las que definió el concepto de función computable.
Church por su parte, inventó un sistema formal al que llamó cálculo lambda, con
el cual definió el concepto de función computable. Las computadoras Von Neumann son conceptualmente máquinas de Turing con registros de acceso aleatorio.
Los lenguajes imperativos como Fortran, Pascal, C y el código ensamblador se
basan en la forma en que las máquinas de Turing son programadas: por medio
de una secuencia de declaraciones (statements). Los lenguajes de programación
funcional como ML, Miranda, están basados en el cálculo lambda. Lisp es un
ejemplo temprano de estos lenguajes (aunque híbrido) e incluso se construyeron
máquinas de reducción para este lenguaje.
1.
Conceptos básicos
Un programa funcional consiste de una expresión E que representa un algoritmo y su entrada. La expresión E es sujeta a una serie de reglas de reescritura.
Una reducción consiste en remplazar una parte P de E por otra expresión P 0
de acuerdo a las reglas de reescritura. En notación esquemática, esto es:
E[P ] → E[P 0 ]
asumiendo que P → P 0 satisface las reglas de reescritura. El proceso de reducción se repite hasta que la expresión resultante no contiene más partes que
puedan ser reescritas. Esta última forma, llamada forma normal E ∗ de la expresión E, constituye la salida del programa.
1
Ejemplo 1 Proceso de reducción en una expresión aritmética. Las reglas de
reescritura correspondientes son las tablas de suma y multiplicación entre numerales.
(7 + 4) × (8 + 5 × 3) → 11 × (8 + 5 × 3)
→ 11 × (8 + 15)
→ 11 × 23
→ 253
Ejemplo 2 Proceso de reducción en una expresión simbólica. Las reglas de reescritura para append y sort pueden especificarse en pocas líneas. Las reglas
como append se conocen como combinadores.
→
→
→
→
firstOf
firstOf
firstOf
firstOf
conejo
(sort (append (perro conejo) (sort (ratón gato))))
(sort (append (perro conejo) (gato ratón)))
(sort (perro conejo gato ratón))
(conejo gato perro ratón)
Los sistemas de reducción normalmente satisfacen la propiedad de ChurchRosser que indica que la forma normal obtenida es independiente de el orden
de evaluación de los subtérminos. De hecho el ejemplo 1 puede reducirse como
sigue:
(7 + 4) × (8 + 5 × 3)
→ (7 + 4) × (8 + 15)
→ 11 × (8 + 15)
→ 11 × 23
→ 253
o incluso evaluando varias expresiones al mismo tiempo:
(7 + 4) × (8 + 5 × 3) →
1.1.
11 × (8 + 15)
→
11 × 23
→
253
Aplicación y abstracción
La primera operación básica del cálculo-λ es la aplicación. La expresión
F.A o F A denotan al dato F considerado como algoritmo, aplicado al dato A
considerado como su entrada. Esto tiene dos interpretaciones, tanto el proceso
de computar F A, como la salida resultante del proceso. La primer interpretación
captura el concepto de reducción, mientras la segunda el de modelo (semántica).
Como se permiten expresiones del tipo F F , es decir F aplicada a sí misma,
se dice que esta teoría es libre de tipos. Esto es útil para simular La segunda
2
operación básica es la abstracción. Si M ≡ M [x] es una expresión que contiene
(depende de) x, entonces λx.M [x] denota la función x 7→ M [x]. La aplicación y
la abstracción trabajan juntas en la siguiente fórmula intuitiva:
(λx,2 × x + 1)3 = 2 × 3 + 1 = 7
esto es, λx. 2 × x + 1 denota la función x 7→ 2 × x + 1 aplicada al argumento
3, dando 2 × 3 + 1 o sea 7. En general tenemos que (λx.M [x])N = M [N ]. La
última ecuación se escribe preferentemente como:
(λx.M )N = M [x := N ]
(1)
donde x := N denota la substitución de x por N
1.2.
Variables libres y acotadas
Se dice que la abstracción acota la variable libre x en M , por ejemplo, en
λx.yx tenemos a x acotada e y es libre. La substitución x := N se lleva a cabo
únicamente en las ocurrencias libres de x:
yx(λx.x)[x := N ] ≡ yN (λx.x)
Por razones de higiene, se asume que siempre las variables acotadas que
ocurren en una expresión son diferentes de las libres. Esto puede cumplirse
renombrando las variables. Por ejemplo, λx.x se convierte en λy.y. De hecho,
estas expresiones actúan de la misma forma:
(λx.x)a = a = (λy.y)a
y de hecho denotan al mismo algoritmo. Por lo tanto, expresiones que varían
sólo en el nombre de las variables acotadas, son equivalentes.
1.3.
Funciones de más argumentos
Funciones de varios argumentos pueden obtenerse iterando la aplicación.
Esta idea se debe a Schönfinkel, pero normalmente se conoce como currying,
por H.B. Curry, quien lo introdujo de manera independiente. Intuitivamente, si
f (x, y) depende de dos argumentos, uno puede definir:
Fx
F
= λy.f (x, y),
= λx.Fx .
entonces:
(F x)y = Fx y = f (x, y)
3
Esta última ecuación muestra que es conveniente usar la asociación a la izquierda cuando tenemos aplicaciones iteradas: F M1 . . . Mn denota (. . . ((F M1 )M2 ) . . . Mn ).
Entonces la ecuación anterior se escribe F xy = f (x, y).
La abstracción iterada, por dualidad, usa asociación a la derecha:
(λx1 . . . xn f (x1 . . . xn ))x1 . . . xn = f (x1 , . . . , xn )
Usando n veces la aplicación β (Eq.??) esta última ecuación puede escribirse
con notación de vector:
→
→
→
→
(λ x .f [ x ]) N = f [N ].
2.
Conversión
En esta sección introduciremos el cálculo-λ formalmente.
Def 1 El conjunto de términos-λ (denotado por Λ) se construye a partir de un
conjunto infinito de variables V = {v, v 0 , v 00 , . . . } usando aplicación y abstracción funcional:
x∈V
⇒
x∈Λ
M, N ∈ Λ
⇒
(M N ) ∈ Λ
M ∈ Λ, x ∈ V
⇒
(λxM ) ∈ Λ
Ejemplo 3 Las siguientes expresiones son términos-λ:
v0
(v 0 v)
(λv(v 0 v))
((λv(v 0 v))v 00 )
(((λv(λv 0 (v 0 v)))v 00 )v 000 )
Por convención x, y, z, . . . denotan variables arbitrarias; M, N, L, . . . denotan términos-λ arbitrarios; Los paréntesis externos no se escriben. M ≡ N
denota que M y N son el mismo término, o que pueden obtenerse uno a partir
del otro renombrando las variables acotadas.
Ejemplo 4 Equivalencia entre términos-λ:
(λxy)z ≡ (λxy)z
(λxx)z ≡ (λyy)z
(λxx)z 6≡ z
(λxx)z 6≡ (λxy)z
4
Usamos también las siguientes abreviaturas: F M1 . . . Mn ≡ (. . . ((F M1 )M2 ) . . . Mn )
y (λx1 . . . xn f (x1 . . . xn ))x1 . . . xn ≡ f (x1 , . . . , xn ). Ahora los términos del ejemplo ??, pueden escribirse como sigue:
Ejemplo 5 Términos-λ escritos siguiendo nuestras convenciones:
y
yx
λx.yx
(λx.yx)z
(λxy.yx)zw
Def 2 El conjunto de variables libres de M , denotado por F V (M ), se define
inductivamente como sigue:
F V (x)
F V (M N )
F V (λx.M )
= {x}
= F V (M ) ∪ F V (N )
= F V (M ) − {x}
Una variable en M está acotada, si no es libre. Observen que una variable
está acotada si ocurre bajo el alcanze de un operador λ. M es un término cerrado
(o combinador) si F V (M ) = ∅. El conjunto de términos-λ cerrados se denota
como Λo .
El resultado de substituir N por las ocurrencias libres de x en M , denotado
por M [x := N ], se define como sigue:
x[x := N ]
=
N
y[x := N ]
=
y, si x 6≡ y
(M1 M2 )[x := N ]
=
(M1 [x := N ]M2 [x := N ])
(λy.M1 )[x := N ]
=
λy.(M1 [x := N ])
Ejemplo 6 Variables libres y acotadas. Consideren el término λxy.xyz; las variables x e y están acotadas; la variable z es libre. El término λxy.xxy está
cerrado.
Por convención, si M1 y M2 ocurren en cierto contexto matemático (una
definición o una prueba), entonces todas las variables acotadas en esos términos,
se eligen para que sean diferentes de las variables libres. Por lo tanto, en la
cláusula 2 de la definición de substitución, no es necesario especificar x 6≡ y.
Ahora podemos introducir el cálculo lambda como una teoría formal.
5
Def 3 El esquema axiomático principal del cálculo-λ, conocido como axioma β,
es:
(λx.M )N = M [x := N ]
(2)
También se incluyen los axiomas de igualdad:
M
M =N
=
M
⇒ N =M
M = N, N = L ⇒ M = L
y reglas de compatibilidad:
M = M0
⇒ M Z = M 0Z
0
⇒ ZM = ZM 0
M =M
M = M0
⇒ λx.M = λx.M 0
Si M = N es demostrable en el cálculo-λ, algunas veces escribimos λ ` M =
N.
Como consecuencia de las reglas de compatibilidad, uno puede remplazar
(sub) términos por términos iguales en cualquier contexto:
M = N ⇒ ...M ··· = ...N ...
Por ejemplo, si λ ` (λy.yy)x = xx, entonces:
λ ` λx.x((λy.yy)x)x = λx.x(xx)x
Lema 1 λ ` (λx1 . . . xn .M )X1 . . . Xn = M [x1 := X1 ] . . . [xn := Xn ].
La demostración es sencilla. Por el axioma β (Ec. ??) tenemos:
(λx1 .M )X1 = M [x1 = X1 ]
luego, por inducción en n, obtenemos el resultado. 2
Ejemplo 7 Combinadores:
I ≡ λx.x
K
K∗
S
≡ λxy.x
≡ λxy.y
≡ λxyz.zx(yz)
6
usando el lemma ??, obtenemos las siguientes ecuaciones:
IM
KM N
= M
= M
K∗M N
= N
SM N L
= M L(N L)
ahora podemos resolver ecuaciones simples.
Teorema 1 Teorema de punto fijo. ∀F ∃XF X = X. Existe un combinador de
punto fijo:
Y ≡ λf.(λx.f (xx))(λx.f (xx))
tal que:
∀F F (YF ) = YF
La demostración pasa por definir W ≡ λx.F (xx) y X ≡ W W . Entonces
tenemos que X ≡ W W ≡ (λx.F (xx))W = F (W W ) ≡ F X. 2
Podemos definir numerales y funciones numéricas en el cálculo-λ:
Def 4 F n (M ) con F ∈ Λ y n ∈ ℵ se define inductivamente como sigue:
F 0 (M ) ≡
F
n+1
M
≡ (F (F n (M ))
Los numerales de Church c0 , c1 , c2 , . . . se definen por:
cn ≡ λf x.f n (x)
Es posible definir los combinadores siguientes:
A+
≡ λxypq.xp(ypq)
A∗
≡ λxyz.x(yz)
Aexp
≡ λxy.yx
Observen que los números de Church están definidos como funciones parciales. Cada numeral espera dos argumentos f y x. Aunque las instancias de
estos argumentos son irrelevantes, es más sencillo interpretar estos números si
asumimos que f es la función sucesor y x es un símbolo representando el cero.
Para ejemplificar esto, asumiendo que nuestro lenguaje Λ es más expresivo que
lo que hemos definido hasta el momento, definamos las siguientes funciones:
S = λr.ding y aplicar r
7
Z = λr.dong
entoces c4 sería ding(ding(ding(ding(dong)))). Claro que si optamos por S como
la función sucesor y Z por el símbolo para cero, tendríamos que c4 sería 1 + 1 +
1 + 1 + 0 = 4.
Ahora, ¿Porqué definimos los combinadores para suma, multiplicación y exponente de esa forma?
8
Descargar