El cálculo SKI es una variante del cálculo Lambda que no utiliza expresiones lambda. En cambio, solo se utilizan la aplicación y los combinadores S , K y yo . En este desafío, su tarea es traducir los términos de SKI en términos de Lambda en forma normal β .
Especificación de entrada
La entrada es un término SKI en la siguiente representación textual. Puede elegir recibir una nueva línea final opcional. La entrada se compone de los caracteres S
, K
, I
, (
, y )
y satisface la siguiente gramática (en forma ABNF) con sterm
ser el símbolo de inicio:
sterm = sterm combinator ; application
sterm = combinator ;
sterm = '(' sterm ')' ; grouping
combinator = 'S' | 'K' | 'I' ; primitives
Especificación de salida
La salida es un término lambda sin variables libres en la siguiente representación textual. Puede optar por generar una nueva línea final opcional. La salida deberá satisfacer la siguiente gramática en forma ABNF con lterm
el símbolo de inicio:
lterm = lterm operand ; application
lterm = ALPHA '.' lterm ; lambda
lterm = operand
operand = '(' lterm ')' ; grouping
operand = ALPHA ; variable (a letter)
Restricciones
Puede suponer que la entrada tiene una forma normal β. Puede suponer que la forma normal β utiliza como máximo 26 variables diferentes. Puede suponer que tanto la entrada como la salida son representables en 79 caracteres.
Entradas de muestra
Aquí hay un par de entradas de muestra. La salida es una salida posible para la entrada dada.
input output
I a.a
SKK a.a
KSK a.b.c.ac(bc)
SII a.aa
Tanteo
La solución más corta en octetos gana. Las lagunas comunes están prohibidas.
sterm
ylterm
usar asociatividad izquierda cuando faltan paréntesis.SKI
comoS(KI)
.Respuestas:
Haskell , 232 bytes
Pruébalo en línea!
Cómo funciona
Esta es una interfaz de analizador diferente a mi respuesta a "Escribir un intérprete para el cálculo lambda sin tipo" , que también tiene una versión no documentada con documentación.
Brevemente,
Term = T (Char -> String)
es el tipo de términos de cálculo lambda, que saben cómo aplicarse a otros términos (a :: Term -> Term -> Term
) y cómo mostrarse como aString
((%) :: Term -> Char -> String
), dada una nueva variable inicial como aChar
. Podemos convertir una función en términos en un término conl :: (Term -> Term) -> Term
, y debido a que la aplicación del término resultante simplemente llama a la función (a (l f) == f
), los términos se reducen automáticamente a la forma normal cuando se muestran.fuente
Rubí, 323 bytes.
No puedo creer que esta mierda funcione en absoluto:
Usar la sustitución de expresiones regulares para realizar la reducción β en cadenas sin procesar es algo de Tony-the-Pony. No obstante, su salida parece correcta al menos para casos de prueba fáciles:
Aquí está manejando
K(K(K(KK)))
con algún resultado de depuración, que toma alrededor de 7 segundos en mi computadora portátil, porque la recursividad de expresión regular es lenta . ¡Puedes ver su conversión α en acción!fuente
Pitón 2, 674
Nota: después
while 1:
, 3 líneas se sangran con un carácter de tabulación.Este es básicamente el código detrás de http://ski.aditsu.net/ , traducido a python, muy simplificado y muy golfizado.
Referencia: (esto probablemente sea menos útil ahora que el código está comprimido)
V = término variable
A = término de aplicación
L = término lambda
c = contador variable
p = reemplazar variable con término
r = reducir
m = renumeración de variable final
u = renumeración de variable interna (para términos duplicados)
s = conversión de cadena
(parámetro s = auto)
C = caracteres separadores para la conversión de cadenas
I, K, S: combinadores
E = análisis
Ejemplos:
(se espera este ↑ porque
SII(SII)
es irreducible)Gracias Mauris y Sp3000 por ayudar a matar un montón de bytes :)
fuente
def m(a,b,c):return foo
enm=lambda a,b,c:foo
incluso dentro de las clases, lo que podría ahorrar una gran cantidad de bytes.a.b.c.a(c)(b(c))
como una expresión lambda válida: ¿qué es(c)
?Lisp común, 560 bytes
"Finalmente, encontré un uso para
PROGV
".Sin golf
Beta-reducción
Las variables se enlazan dinámicamente durante la reducción con los
PROGV
nuevos símbolos Common Lisp, usandoMAKE-SYMBOL
. Esto permite evitar muy bien las colisiones de nombres (por ejemplo, sombreado no deseado de variables enlazadas). Podría haberlo usadoGENSYM
, pero queremos tener nombres fáciles de usar para los símbolos. Es por eso que los símbolos se nombran con letras de aa z(según lo permitido por la pregunta).N
representa el código de caracteres de la siguiente letra disponible en el alcance actual y comienza con 97, también conocido comoa .Aquí hay una versión más legible de
R
(sin laW
macro):Resultados intermedios
Analizar desde la cuerda:
Reducir:
(Ver rastro de ejecución)
Bonito estampado:
Pruebas
Reutilizo el mismo conjunto de pruebas que la respuesta de Python:
El octavo ejemplo de prueba es demasiado grande para la tabla anterior:
a.a.a.a.a.b.a
es correcto y no utiliza tanto las letras como la respuesta Python, donde a las consolidacionesa
,b
,c
yd
no se hace referencia.Actuación
Recorrer las 7 pruebas de aprobación anteriores y recopilar los resultados es inmediato (salida SBCL):
Hacer la misma prueba cientos de veces conduce a ... "Almacenamiento local de subprocesos agotado" en SBCL, debido a una limitación conocida con respecto a variables especiales. Con CCL, llamar al mismo conjunto de pruebas 10000 veces lleva 3,33 segundos.
fuente