(Asegúrese de entender higher-order functions). En Alonzo Churchuntyped lambda calculus, una función es el único tipo de datos primitivo. No hay números, booleanos, listas o cualquier otra cosa, solo funciones. Las funciones pueden tener solo 1 argumento, pero las funciones pueden aceptar y/o devolver funciones, no valores de estas funciones, sino funciones mismas. Por lo tanto, para representar números, booleanos, listas y otros tipos de datos, debe encontrar una forma inteligente para que funciones anónimas los representen. Church numerals es la forma de representar natural numbers. Tres construcciones más primitivas en el cálculo lambda sin tipo son:
λx.x
, una identity function, acepta alguna función e inmediatamente devuelve.
λx.x x
, autoaplicación.
λf.λx.f x
, aplicación de función, toma una función y un argumento, y aplica una función a un argumento.
¿Cómo se codifica 0, 1, 2 como nada más que funciones? De alguna manera necesitamos construir la noción de cantidad en el sistema. Solo tenemos funciones, cada función se puede aplicar solo a 1 argumento. ¿Dónde podemos ver algo parecido a la cantidad? ¡Oye, podemos aplicar una función a un parámetro varias veces! Obviamente hay una sensación de cantidad en 3 invocaciones repetidas de una función: f (f (f x))
. Así que vamos a codificar en el cálculo lambda:
- 0 =
λf.λx.x
- 1 =
λf.λx.f x
- 2 =
λf.λx.f (f x)
- 3 =
λf.λx.f (f (f x))
Y así sucesivamente. ¿Pero cómo se pasa de 0 a 1, o de 1 a 2? ¿Cómo escribirías una función que, dado un número, devolvería un número incrementado en 1?Vemos el patrón en Números de la Iglesia que el término siempre comienza con λf.λx.
y después de que tiene una aplicación repetida finita de f, así que tenemos que entrar de alguna manera en el cuerpo de λf.λx.
y envolverlo en otro f
. ¿Cómo se cambia un cuerpo de una abstracción sin reducción? Bueno, puedes aplicar una función, envolver el cuerpo en una función y luego envolver el nuevo cuerpo en la antigua abstracción lambda. Pero no desea que los argumentos cambien, por lo tanto, aplica abstracciones a los valores del mismo nombre: ((λf.λx.f x) f) x → f x
, pero ((λf.λx.f x) a) b) → a b
, que no es lo que necesitamos.
Por eso es add1
λn.λf.λx.f ((n f) x)
: aplicar n
a f
y luego x
para reducir la expresión al cuerpo, a continuación, aplicar f
a ese cuerpo, entonces abstracta de nuevo con λf.λx.
. Ejercicio: demasiado ver que es verdad, aprender rápidamente y reducir β-reduction(λn.λf.λx.f ((n f) x)) (λf.λx.f (f x))
al incremento de 2 por 1.
Ahora la comprensión de la intuición detrás de envolver el cuerpo en otra función de invocación, ¿cómo ponemos en práctica la adición de 2 números? Necesitamos una función que, dado λf.λx.f (f x)
(2) y λf.λx.f (f (f x))
(3), devolvería λf.λx.f (f (f (f (f x))))
(5). Mire 2. ¿Qué pasaría si pudiera reemplazar su x
con el cuerpo de 3, es decir f (f (f x))
? Para obtener el cuerpo de 3, es obvio, simplemente aplíquelo al f
y luego al x
. Ahora aplique 2 a f
, pero luego aplíquelo al cuerpo de 3, no a x
. Luego envuélvalo en λf.λx.
nuevamente: λa.λb.λf.λx.a f (b f x)
.
Conclusión: Para añadir los números 2 y a
b
juntos, ambos de los cuales se representan como números de la Iglesia, que desea reemplazar x
en a
con el cuerpo de b
, de manera que f (f x)
+ f (f (f x))
= f (f (f (f (f x))))
. Para que esto suceda, aplique a
al f
, luego al b f x
.
Ok, eso tiene sentido. Gracias Eli. Estaba haciendo lo correcto y tratando de hacer sustituciones en el procedimiento add-1 para obtener la función más. Me di cuenta de la relación (f^m (f^n x)), pero de forma estúpida no hice el salto de eso a ((m f) ((n f) x)), lo cual es obvio ahora que lo pienso. –