O desafio é escrever um intérprete para o cálculo lambda não digitado com o mínimo de caracteres possível. Definimos o cálculo lambda sem tipo da seguinte maneira:
Sintaxe
Existem os três tipos de expressões a seguir:
Uma expressão lambda tem o formato
(λ x. e)
ondex
pode haver qualquer nome de variável legal ee
qualquer expressão legal. Aquix
é chamado de parâmetro ee
é chamado de corpo da função.Por uma questão de simplicidade, adicionamos a restrição adicional de que não deve haver uma variável com o mesmo nome que
x
atualmente no escopo. Uma variável começa a estar no escopo quando seu nome aparece entre(λ
e.
e para no escopo correspondente)
.- O aplicativo de funções tem a forma
(f a)
ondef
ea
são expressões legais. Aquif
é chamado de função ea
é chamado de argumento. - Uma variável tem o formato em
x
quex
é um nome de variável legal.
Semântica
Uma função é aplicada substituindo cada ocorrência do parâmetro no corpo das funções por seu argumento. Mais formalmente uma expressão da forma ((λ x. e) a)
, em que x
é um nome da variável e e
e a
são expressões, avalia (ou reduz) para a expressão e'
em que e'
é o resultado da substituição de cada ocorrência de x
em e
com a
.
Uma forma normal é uma expressão que não pode ser avaliada mais.
O desafio
Sua missão, se você optar por aceitá-la, é escrever um intérprete que tenha como entrada uma expressão do cálculo lambda não digitado que não contém variáveis livres e produz como saída a forma normal da expressão (ou uma expressão alfa-congruente a ela) . Se a expressão não tiver uma forma normal ou não for uma expressão válida, o comportamento será indefinido.
A solução com o menor número de caracteres vence.
Algumas notas:
- A entrada pode ser lida a partir de stdin ou de um nome de arquivo fornecido como argumento de linha de comando (você só precisa implementar um ou outro - e não ambos). A saída vai para stdout.
- Como alternativa, você pode definir uma função que recebe a entrada como uma string e retorna a saída como uma string.
- Se caracteres não ASCII forem problemáticos para você, você poderá usar o
\
caractere barra invertida ( ) em vez de λ. - Contamos o número de caracteres, não bytes, portanto, mesmo que seu arquivo de origem seja codificado como unicode, λ conta como um caractere.
- Os nomes de variáveis legais consistem em uma ou mais letras minúsculas, ou seja, caracteres entre a e z (não é necessário suportar nomes alfanuméricos, maiúsculas ou letras não latinas - embora isso não invalide sua solução, é claro).
- No que diz respeito a esse desafio, nenhum parênteses é opcional. Cada expressão lambda e cada aplicativo de função serão cercados por exatamente um par de parênteses. Nenhum nome de variável estará entre parênteses.
- Açúcar sintático como escrever
(λ x y. e)
para(λ x. (λ y. e))
não precisa ser suportado. - Se uma profundidade de recursão superior a 100 for necessária para avaliar uma função, o comportamento será indefinido. Isso deve ser mais do que baixo o suficiente para ser implementado sem otimização em todos os idiomas e ainda grande o suficiente para poder executar a maioria das expressões.
- Você também pode assumir que o espaçamento será como nos exemplos, ou seja, não há espaços no início e no final da entrada ou antes de um
λ
ou.
e exatamente um espaço após ae.
entre uma função e seu argumento e após aλ
.
Entrada e Saída de Amostra
Entrada:
((λ x. x) (λ y. (λ z. z)))
Resultado:
(λ y. (λ z. z))
Entrada:
(λ x. ((λ y. y) x))
Resultado:
(λ x. x)
Entrada:
((λ x. (λ y. x)) (λ a. a))
Resultado:
(λ y. (λ a. a))
Entrada:
(((λ x. (λ y. x)) (λ a. a)) (λ b. b))
Resultado:
(λ a. a)
Entrada:
((λ x. (λ y. y)) (λ a. a))
Resultado:
(λ y. y)
Entrada:
(((λ x. (λ y. y)) (λ a. a)) (λ b. b))
Resultado:
(λ b. b)
Entrada:
((λx. (x x)) (λx. (x x)))
Saída: qualquer coisa (este é um exemplo de expressão que não possui forma normal)
Entrada:
(((λ x. (λ y. x)) (λ a. a)) ((λx. (x x)) (λx. (x x))))
Saída:
(λ a. a)
(este é um exemplo de expressão que não se normaliza se você avaliar os argumentos antes da chamada da função e, infelizmente, um exemplo para o qual minha tentativa de solução falha)Entrada:
((λ a. (λ b. (a (a (a b))))) (λ c. (λ d. (c (c d)))))
Saída:
`(λ a. (λ b. (a (a (a (a (a (a (a (a b))))))))))
Calcula 2 ^ 3 em numerais da igreja.
(\y. a)
.