El desafío es escribir un intérprete para el cálculo lambda sin tipo en la menor cantidad de caracteres posible. Definimos el cálculo lambda sin tipo de la siguiente manera:
Sintaxis
Existen los siguientes tres tipos de expresiones:
Una expresión lambda tiene la forma
(λ x. e)
dondex
podría ser cualquier nombre de variable legal ye
cualquier expresión legal. Aquíx
se llama parámetro ye
se llama cuerpo de función.En aras de la simplicidad, agregamos la restricción adicional de que no debe haber una variable con el mismo nombre que
x
actualmente está en el alcance. Una variable comienza a estar dentro del alcance cuando su nombre aparece entre(λ
y.
y deja de estar dentro del alcance en el correspondiente)
.- La aplicación de función tiene la forma
(f a)
dondef
ya
son expresiones legales. Aquíf
se llama función ya
se llama argumento. - Una variable tiene la forma
x
dondex
es un nombre de variable legal.
Semántica
Una función se aplica reemplazando cada aparición del parámetro en el cuerpo de las funciones con su argumento. Más formalmente una expresión de la forma ((λ x. e) a)
, donde x
es un nombre de variable y e
y a
son expresiones, evalúa (o reduce) la expresión e'
, donde e'
es el resultado de reemplazar cada aparición de x
en e
con a
.
Una forma normal es una expresión que no puede evaluarse más.
El reto
Su misión, si elige aceptarla, es escribir un intérprete que tome como entrada una expresión del cálculo lambda sin tipo que no contenga variables libres y produzca como salida la forma normal de la expresión (o una expresión alfa-congruente con ella) . Si la expresión no tiene forma normal o no es una expresión válida, el comportamiento es indefinido.
La solución con el menor número de caracteres gana.
Un par de notas:
- La entrada puede leerse desde stdin o desde un nombre de archivo dado como argumento de línea de comando (solo necesita implementar uno u otro, no ambos). La salida va a stdout.
- Alternativamente, puede definir una función que tome la entrada como una cadena y devuelva la salida como una cadena.
- Si los caracteres que no son ASCII son problemáticos para usted, puede usar el carácter de barra diagonal inversa (
\
) en lugar de λ. - Contamos el número de caracteres, no bytes, por lo que incluso si su archivo fuente está codificado como ico unicode cuenta como un carácter.
- Los nombres de variables legales consisten en una o más letras minúsculas, es decir, caracteres entre a y z (no es necesario admitir nombres alfanuméricos, letras mayúsculas o letras no latinas, aunque hacerlo no invalidará su solución, por supuesto).
- En lo que respecta a este desafío, no hay paréntesis opcionales. Cada expresión lambda y cada aplicación de función estarán rodeadas exactamente por un par de paréntesis. Ningún nombre de variable estará rodeado de paréntesis.
- Azúcar sintáctico como escribir
(λ x y. e)
para(λ x. (λ y. e))
no necesita ser apoyada. - Si se requiere una profundidad de recursión de más de 100 para evaluar una función, el comportamiento es indefinido. Eso debería ser más que lo suficientemente bajo como para implementarse sin optimización en todos los idiomas y aún lo suficientemente grande como para poder ejecutar la mayoría de las expresiones.
- También puede suponer que el espaciado será como en los ejemplos, es decir, sin espacios al principio y al final de la entrada o antes de
λ
ao.
y exactamente un espacio después de ay.
entre una función y su argumento y después de aλ
.
Muestra de entrada y salida
Entrada:
((λ x. x) (λ y. (λ z. z)))
Salida:
(λ y. (λ z. z))
Entrada:
(λ x. ((λ y. y) x))
Salida:
(λ x. x)
Entrada:
((λ x. (λ y. x)) (λ a. a))
Salida:
(λ y. (λ a. a))
Entrada:
(((λ x. (λ y. x)) (λ a. a)) (λ b. b))
Salida:
(λ a. a)
Entrada:
((λ x. (λ y. y)) (λ a. a))
Salida:
(λ y. y)
Entrada:
(((λ x. (λ y. y)) (λ a. a)) (λ b. b))
Salida:
(λ b. b)
Entrada:
((λx. (x x)) (λx. (x x)))
Salida: cualquier cosa (este es un ejemplo de una expresión que no tiene forma normal)
Entrada:
(((λ x. (λ y. x)) (λ a. a)) ((λx. (x x)) (λx. (x x))))
Salida:
(λ a. a)
(Este es un ejemplo de una expresión que no se normaliza si evalúa los argumentos antes de la llamada a la función, y lamentablemente un ejemplo para el que falla mi intento de solución)Entrada:
((λ a. (λ b. (a (a (a b))))) (λ c. (λ d. (c (c d)))))
Salida:
`(λ a. (λ b. (a (a (a (a (a (a (a (a b))))))))))
Esto calcula 2 ^ 3 en números de la Iglesia.
(\y. a)
.