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)ondexpode haver qualquer nome de variável legal eequalquer 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
xatualmente 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)ondefeasão expressões legais. Aquifé chamado de função eaé chamado de argumento. - Uma variável tem o formato em
xquexé 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 ee asão expressões, avalia (ou reduz) para a expressão e'em que e'é o resultado da substituição de cada ocorrência de xem ecom 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).