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.
fonte
(\y. a)
.Respostas:
O mais novo:
Aumentei para 644 caracteres , considerei partes de cEll em cOpy e Par; chamadas armazenadas em cache para célula e cdr em variáveis locais temporárias e movidas essas variáveis locais para globais em funções "terminais" (isto é, não recursivas). Além disso, constantes decimais são mais curtas que literais de caracteres e esse negócio desagradável ...
... identifica corretamente letras minúsculas (assumindo ASCII), mas também aceita qualquer um dos caracteres `{|} ~. (Essa mesma observação sobre ASCII é feita neste excelente vídeo sobre UTF-8 .)
Et viola: |
Anteriormente:
Posso obter alguns votos por esforço? Estou trabalhando dia e noite há uma semana. Desenterrei o artigo original de McCarthy e fui atormentado por um inseto no próprio artigo até ler o apêndice de The Roots of Lisp, de Paul Graham . Fiquei tão distraído que me tranquei fora da minha casa, depois esqueci completamente até chegar em casa novamente às 12:30 (um pouco tarde para ligar para o gerente do prédio que mora no condado) e tive que passar o tempo. noite na casa da minha avó (cortando até que a bateria do meu laptop estivesse seca).
E depois de tudo isso, não é nem perto da entrada vencedora!
Não sei como diminuir isso; e usei todos os truques sujos que consigo imaginar! Talvez não possa ser feito em C.
Com alguma generosidade na contagem (o primeiro pedaço pega uma corda e imprime o resultado), são
778770709694 caracteres. Mas, para torná-lo independente, é preciso ter essasbrk
ligação. E para lidar com expressões mais complicadas, ele também precisa dosignal
manipulador. E é claro que não pode ser transformado em um módulo com qualquer código que tente usarmalloc
.Então, infelizmente, aqui está:
Aqui está o bloco antes das reduções finais. Os truques aqui são cursores inteiros em vez de ponteiros (aproveitando o comportamento 'implícito') e o uso de 'memória temporária': o
char*n
é o ponteiro 'novo' ou 'próximo' no espaço livre. Mas, às vezes, escrevo uma string na memória, depois chamo strlen e incrementamos n; efetivamente usando memória e , em seguida, alocá-lo, após o tamanho é mais fácil de calcular. Você pode ver que é praticamente direto do artigo de McCarthy, com exceção decell()
quais interfaces entre as funções e a representação de strings dos dados.fonte
sprintf(n,...);n+=strlen(n)+1;
é melhor comon+=sprintf(n,...)+1;
Inverter a sintaxe da matriz emx[m]
vez dem[x]
me permitir substituir todos os indiretos por uma macro 'postfix'#define M [m]
...x M
que salva 1 caractere e dá uma quebra de linha "livre", pois é necessário espaço em branco para separar os tokens.// um ...
faz um loop pela seqüência e conta parênteses até encontrar o parêntese correspondente no nível de aninhamento correto.Cálculo binário Lambda 186
O programa mostrado no dump hexadecimal abaixo
não aceita exatamente o formato que você propõe. Em vez disso, espera um termo lambda no formato binário lambda calculus (blc). No entanto, ele mostra todas as etapas na redução da forma normal, usando parênteses mínimos.
Exemplo: computando 2 ^ 3 em numerais da igreja
Salve o dump hexadecimal acima com xxd -r> symbolic.Blc
Pegue um intérprete do blc em http://tromp.github.io/cl/uni.c
Como o hexdump é bastante ilegível, aqui está uma versão "desmontada"
substituindo 00 (lambda) por \ e 01 (aplicativo) por @ Agora é quase tão legível quanto brainfuck :-)
Veja também http://www.ioccc.org/2012/tromp/hint.html
fonte
Haskell,
342323317305 caracteresAté o momento em que este artigo foi escrito, essa é a única solução que avalia ((λ f. (Λ x. (Fx))) (λ y. (Λ x. Y))) para o resultado correto (λ x. (Λ z. x)) em vez de (λ x. (λ x. x)). A implementação correta do cálculo lambda requer substituição para evitar capturas , mesmo com a garantia simplificadora desse problema de que nenhuma variável oculta outra variável em seu escopo. (Meu programa funciona mesmo sem essa garantia.)
Notas:
Versão ungolfed com documentação.
fonte
Python -
321320Aqui está minha tentativa (fixa):
fonte
Ruby 254 caracteres
Pode ser usado como
A solução ainda não está totalmente disponível, mas já está quase ilegível.
fonte
Edit: verifique minha resposta abaixo para 250 sob JavaScript puro.
2852243 caracteres usando LiveScript (sem Regex! Não totalmente golfe - pode ser melhorado)Teste:
Ou seja
3^2=9
, conforme declarado no OP.Se alguém estiver curioso, aqui está uma versão estendida com alguns comentários:
fonte
Arco Waterhouse - 140 caracteres
fonte
Bytes C 1039
As variáveis permitem como entrada usando letras minúsculas [de a..z] o sys pode gerar variáveis usando letras maiúsculas [de A..Z] se necessário na saída ... Assuma a configuração de caracteres ascii.
fonte
Haskell 456 C
Pode ser muito menor se o recurso de avaliação lenta do Haskell for totalmente utilizado. Infelizmente, eu não sei como fazer isso.
Além disso, muitos caracteres são desperdiçados na etapa de análise.
Versão ungolfed
fonte
Obteve 231 com JavaScript / sem Regex
Recebe matrizes de 2 elementos.
1
representaλ
e 2 representa uma variável de índice bruijn.Teste:
fonte
Python: 1266 caracteres (medido usando o wc)
Não é o mais curto em termos gerais, mas lida corretamente com a renomeação alfa e todos os exemplos listados na postagem dos OPs.
fonte
except NameError
com apenasexcept
? (3) Os nomes de função de dois caracteres podem ser renomeados para nomes de um caractere. (4) Existem alguns lugares onde você tem atribuições que têm espaços ao redor do=
. (5)if t[0]=='c'
pode ser substituído porif'c'==t[0]
.C ++ (gcc) ,
782766758731 bytesExperimente online!
A idéia básica aqui é que o código use uma representação interna baseada na idéia dos índices de De Bruijn - exceto que eu inverto os índices para indicar a profundidade lambda da ligação da variável referida. No código:
E::t
representa o tipo de um nó - 0 para um nó folha variável, 1 para um nó lambda e 2 para um nó de aplicativo de função. (Escolhido para coincidir com a aridade do nó, o que por acaso é possível.) Então,E::l
eE::r
são os filhos conforme apropriado (apenasE::l
para um nó lambda) eE::i
é o índice de profundidade lambda para um nó folha variável.E::E(E&o,int d,int e)
clona uma subexpressão que estava inicialmente na profundidade lambdad
para colar em um novo local na profundidade lambdad+e
. Isso envolve preservar variáveis na profundidade lambda menor qued
ao incrementar variáveis na profundidade lambda pelo menosd
eme
.E::s
faz uma substituição da subexpressãov
em número variáveld
no*this
tempo variável decrementing números maiores do qued
(ee
é um detalhe interno rastrear o incremento lambda profundidade para quando se necessita de chamadaE::c
).E::R
procura uma única redução beta para executar, preferindo as instâncias superior ou esquerda, de acordo com uma pesquisa de pré-venda no AST. Ele retorna diferente de zero se encontrou uma redução para executar ou zero se não encontrou nenhuma.E::u
é umato_string
operação de tipo que reconstitui uma string "legível por humanos" usando nomes sintéticos para as variáveis. (Observe que, devido a um pouco de golfe naV
função auxiliar, ela só gera nomes contendoa
através delai
.)E::E(int d, std::map<std::string, int> m, char*&s)
analisa uma sequência de entradas
em uma expressão AST com base em um mapeamentom
de nomes de variáveis atualmente vinculados em índices de profundidade lambda.f
é a principal função que responde à pergunta.(Como você pode ver no link TIO, o código faz nomes de variáveis punho com vários personagens, e que também recebe uma resposta correta de
(\ a. (\ b. a))
para((\ f. (\ x. (f x))) (\ y. (\ x. y)))
. Também acontece que o código de análise pode lidar com sombreamento variável, sem nenhum custo extra.)-16 bytes parcialmente devido à idéia de ceilingcat (que eu também inventei de forma independente) e parcialmente devido à alteração
E*a=new E;
paraE&a=*new E;
e depois a alteraçãoa->
paraa.
-8 bytes a mais devido a outro comentário de ceilingcat (atribuição de fator de
a.t
saída ternário)-27 bytes da conversão de analisador e clone em construtores de
E
fonte
Bytes C 637
Esta versão não usa variáveis auxiliares (portanto, não segue 100% do que o cálculo lambda diz ... como muitas outras aqui ...). Cada variável deve ter 1 caractere de comprimento (como algumas outras aqui). Código do teste:
resultados:
este é o semi-ungolf:
fonte