Le défi consiste à écrire un interprète pour le calcul lambda non typé dans le moins de caractères possible. Nous définissons le calcul lambda non typé comme suit:
Syntaxe
Il existe trois types d'expressions:
Une expression lambda a la forme
(λ x. e)
oùx
pourrait être n'importe quel nom de variable juridique ete
toute expression juridique. Icix
s'appelle le paramètre ete
s'appelle le corps de la fonction.Par souci de simplicité, nous ajoutons la restriction supplémentaire selon laquelle il ne doit pas y avoir de variable portant le même nom que celle
x
actuellement dans la portée. Une variable commence à se trouver dans la portée lorsque son nom apparaît entre(λ
et.
et cesse de se trouver dans la portée correspondante)
.- L'application de fonction a la forme
(f a)
oùf
eta
sont des expressions juridiques. Icif
s'appelle la fonction eta
s'appelle l'argument. - Une variable a la forme
x
oùx
est un nom de variable légal.
Sémantique
Une fonction est appliquée en remplaçant chaque occurrence du paramètre dans le corps de la fonction par son argument. Plus formellement, une expression de la forme ((λ x. e) a)
, où x
est un nom de variable et e
et a
sont des expressions, est évaluée (ou réduite) à l'expression e'
où e'
est le résultat du remplacement de chaque occurrence de x
in e
par a
.
Une forme normale est une expression qui ne peut plus être évaluée.
Le défi
Votre mission, si vous l'acceptez, est d'écrire un interpréteur qui prend en entrée une expression du calcul lambda non typé ne contenant aucune variable libre et produit en sortie la forme normale de l'expression (ou une expression qui lui correspond parfaitement) . Si l'expression n'a pas de forme normale ou s'il ne s'agit pas d'une expression valide, le comportement n'est pas défini.
La solution avec le plus petit nombre de caractères gagne.
Quelques notes:
- Les entrées peuvent être lues à partir de stdin ou d'un nom de fichier donné en argument de ligne de commande (vous devez uniquement implémenter l'un ou l'autre, pas les deux). La sortie passe à stdout.
- Vous pouvez également définir une fonction qui prend l’entrée sous forme de chaîne et renvoie le résultat sous forme de chaîne.
- Si les caractères non-ASCII vous posent problème, vous pouvez utiliser le caractère barre oblique inverse (
\
) au lieu de λ. - Nous comptons le nombre de caractères, pas d'octets, donc même si votre fichier source est codé sous la forme d'unicode, λ compte pour un caractère.
- Les noms de variables légales consistent en une ou plusieurs lettres minuscules, c'est-à-dire des caractères compris entre a et z (inutile de prendre en charge les noms alphanumériques, les majuscules ou les lettres non latines - bien que cela n'invalidera pas votre solution, bien sûr).
- En ce qui concerne ce défi, aucune parenthèse n'est optionnelle. Chaque expression lambda et chaque application de fonction seront entourées d’exactement une paire de parenthèses. Aucun nom de variable ne sera entouré de parenthèses.
- Le sucre syntaxique comme écrire
(λ x y. e)
pour(λ x. (λ y. e))
n'a pas besoin d'être soutenu. - Si une profondeur de récursivité supérieure à 100 est requise pour évaluer une fonction, le comportement n'est pas défini. Cela devrait être plus que suffisamment bas pour être implémenté sans optimisation dans toutes les langues et toujours assez grand pour pouvoir exécuter la plupart des expressions.
- Vous pouvez également supposer que l'espacement sera comme dans les exemples, c'est-à-dire pas d'espaces au début et à la fin de l'entrée ou avant un
λ
ou.
et exactement un espace après un.
et entre une fonction et son argument et après unλ
.
Exemple d'entrée et de sortie
Contribution:
((λ x. x) (λ y. (λ z. z)))
Sortie:
(λ y. (λ z. z))
Contribution:
(λ x. ((λ y. y) x))
Sortie:
(λ x. x)
Contribution:
((λ x. (λ y. x)) (λ a. a))
Sortie:
(λ y. (λ a. a))
Contribution:
(((λ x. (λ y. x)) (λ a. a)) (λ b. b))
Sortie:
(λ a. a)
Contribution:
((λ x. (λ y. y)) (λ a. a))
Sortie:
(λ y. y)
Contribution:
(((λ x. (λ y. y)) (λ a. a)) (λ b. b))
Sortie:
(λ b. b)
Contribution:
((λx. (x x)) (λx. (x x)))
Sortie: n'importe quoi (Ceci est un exemple d'expression qui n'a pas de forme normale)
Contribution:
(((λ x. (λ y. x)) (λ a. a)) ((λx. (x x)) (λx. (x x))))
Résultat:
(λ a. a)
(Ceci est un exemple d'expression qui ne se normalise pas si vous évaluez les arguments avant l'appel de la fonction et, malheureusement, un exemple pour lequel ma tentative de solution a échoué.)Contribution:
((λ a. (λ b. (a (a (a b))))) (λ c. (λ d. (c (c d)))))
Sortie:
`(λ a. (λ b. (a (a (a (a (a (a (a (a b))))))))))
Ceci calcule 2 ^ 3 en chiffres d'église.
(\y. a)
.Réponses:
Le plus récent:
Je l'ai réduit à 644 caractères , j'ai factorisé des parties de cEll dans cOpy et Par; Mise en cache des appels à cell et cdr dans des variables locales temporaires et déplacement de ces variables locales vers des globales dans des fonctions "terminal" (c'est-à-dire non récursives). En outre, les constantes décimales sont plus courtes que les littéraux de caractères et cette mauvaise affaire ...
... identifie correctement les lettres minuscules (en supposant que ASCII), mais accepte également n'importe lequel des `{|} ~. (Cette même observation à propos d'ASCII est faite dans cette excellente vidéo sur UTF-8 .)
Et alto: |
Plus tôt:
Puis-je obtenir quelques votes pour l'effort? Je travaille ce jour et cette nuit depuis une semaine. J'ai extrait le papier original de McCarthy et j'ai été affecté par un bug dans le papier lui-même jusqu'à ce que je lise l'annexe de The Roots of Lisp de Paul Graham . J'étais tellement distraite que je me suis enfermée à l'extérieur de ma maison, puis complètement oubliée jusqu'à mon retour à la maison ce soir-là à 12 h 30 (un peu tard pour appeler le gérant de l'immeuble qui vit très loin dans le comté) et j'ai dû passer le nuit chez ma grand-mère (piratage jusqu’à ce que la batterie de mon ordinateur portable soit sèche).
Et après tout cela, ce n'est même pas proche de l'entrée gagnante!
Je ne suis pas sûr de savoir comment rendre cela plus court; et j'ai utilisé tous les sales tours auxquels je peux penser! Peut-être que cela ne peut pas être fait en C.
Avec une certaine générosité dans le comptage (le premier morceau prend une chaîne et affiche le résultat), il s'agit de
778770709694 caractères. Mais pour le rendre autonome, il doit avoir cetsbrk
appel. Et pour gérer des expressions plus complexes, il a également besoin dusignal
gestionnaire. Et bien sûr, il ne peut pas être transformé en un module avec un code qui tente de l'utilisermalloc
.Alors, hélas, la voici:
Voici le bloc juste avant les dernières réductions. Les astuces ici sont des curseurs de nombre entier au lieu de pointeurs (tirant parti du comportement "implicite") et de l'utilisation de "mémoire de travail": il
char*n
s'agit du pointeur "nouveau" ou "suivant" dans l'espace libre. Mais parfois, j'écris une chaîne dans la mémoire, puis j'appelle strlen et incrémente n; en utilisant efficacement la mémoire, puis en l' attribuant, il est plus facile de calculer la taille. Vous pouvez voir que c'est assez directement du papier McCarthy, à l'exception descell()
interfaces entre les fonctions et la représentation sous forme de chaîne de données.la source
sprintf(n,...);n+=strlen(n)+1;
est préférable carn+=sprintf(n,...)+1;
Inverser la syntaxe du tableaux[m]
au lieu dem[x]
me permettre de remplacer tous les indirections par une macro 'postfixe'#define M [m]
...x M
qui enregistre 1 caractère et donne un retour à la ligne "libre" car un espace est nécessaire pour séparer les jetons.// um ...
parcourt la chaîne et compte entre parenthèses jusqu'à ce qu'il trouve la parence proche correspondante au niveau d'imbrication correct.Lambda Calcul binaire 186
Le programme affiché dans le vidage hexadécimal ci-dessous
n'accepte pas tout à fait le format que vous proposez. Au contraire, il attend un terme lambda au format binaire lambda calcul (blc). Cependant, il montre chaque étape de la réduction de la forme normale, en utilisant des parenthèses minimales.
Exemple: calculer 2 ^ 3 en chiffres d'église
Enregistrez le vidage hexadécimal ci-dessus avec xxd -r> symbolic.Blc
Prenez un interpréteur blc depuis http://tromp.github.io/cl/uni.c
Puisque l'hexdump est assez illisible, voici une version "désassemblée"
remplacer 00 (lambda) par \ et 01 (application) par @ Maintenant, il est presque aussi lisible que brainfuck :-)
Voir aussi http://www.ioccc.org/2012/tromp/hint.html
la source
Haskell,
342323317305 caractèresAu moment d'écrire ces lignes, c'est la seule solution qui évalue ((λ f. (Λ x. (Fx))) (λ y. (Λ x. Y))) au résultat correct (λ x. (Λ z. x)) plutôt que (λ x. (λ x. x)). La mise en œuvre correcte du lambda calcul nécessite une substitution sans capture , même avec la garantie de simplification de ce problème qu'aucune variable ne masque une autre variable dans son étendue. (Mon programme fonctionne même sans cette garantie.)
Remarques:
Version non-golfée avec documentation.
la source
Python -
321320Voici ma tentative (corrigée):
la source
Ruby 254 personnages
Il peut être utilisé comme
La solution n’est pas encore totalement jouée au golf mais déjà presque illisible.
la source
Edit: vérifiez ma réponse ci-dessous pour 250 sous JavaScript pur.
2852243 caractères utilisant LiveScript (Pas de Regex! Pas complètement joué au golf - pourrait être amélioré)Tester:
Ce qui est
3^2=9
, comme indiqué sur OP.Si quelqu'un est curieux, voici une version étendue avec quelques commentaires:
la source
Waterhouse Arc - 140 personnages
la source
C 1039 octets
Les variables permettent une entrée utilisant des lettres minuscules [de a..z]. Le système peut générer des variables avec des lettres majuscules [de A..Z] si nécessaire dans la sortie ... Supposons la configuration de caractères ascii.
la source
Haskell 456 C
Cela peut être beaucoup plus court si la fonctionnalité d'évaluation paresseuse de Haskell est pleinement utilisée. Malheureusement, je ne sais pas comment faire.
En outre, de nombreux caractères sont perdus lors de l’analyse.
Version non-golfée
la source
Vous avez 231 avec JavaScript / no Regex
Reçoit des tableaux à 2 éléments.
1
représenteλ
2 et correspond à une variable d'index de Bruijn.Tester:
la source
Python: 1266 caractères (mesurés avec wc)
Pas le plus court de loin, mais il gère correctement le renommage alpha et tous les exemples énumérés dans les PO post.
la source
except NameError
par justeexcept
? (3) Les noms de fonction à deux caractères peuvent être renommés en noms à un caractère. (4) Il existe quelques endroits où vous avez des tâches qui ont des espaces autour du=
. (5)if t[0]=='c'
peut être remplacé parif'c'==t[0]
.C ++ (gcc) ,
782766758731 octetsEssayez-le en ligne!
L'idée de base est que le code utilise une représentation interne basée sur l'idée des indices de Bruijn - sauf que j'inverse les indices pour indiquer la profondeur lambda de la liaison de la variable mentionnée. Dans le code:
E::t
représente le type d'un nœud - 0 pour un nœud feuille variable, 1 pour un nœud lambda et 2 pour un nœud d'application de fonction. (Choisi de manière à ce qu'il coïncide avec l'arité du nœud, ce qui est tout à fait possible.) EnsuiteE::l
,E::r
les enfants sont appropriés (E::l
pour un nœud lambda uniquement ) etE::i
constitue l'indice de profondeur lambda d'un nœud feuille variable.E::E(E&o,int d,int e)
clone une sous-expression qui était initialement à la profondeur lambdad
pour pouvoir être collée dans un nouvel emplacement à la profondeur lambdad+e
. Cela implique la préservation des variables à la lambda-profondeur inférieured
tout en incrémentant variables à lambda profondeur au moinsd
pare
.E::s
effectue une substitution de la sous-expressionv
en nombre variabled
en*this
décrémentant les nombres variables supérieurs àd
(ete
est un détail interne suivi de l'incrément de profondeur lambda pour le moment où il doit appelerE::c
).E::R
recherche une seule réduction bêta à effectuer, en préférant les instances les plus en haut ou les plus à gauche selon une recherche en pré-commande via l'AST. Il renvoie une valeur différente de zéro s'il trouve une réduction à effectuer ou zéro si aucune.E::u
est uneto_string
opération de type qui reconstitue une chaîne "lisible par l'homme" en utilisant des noms synthétiques pour les variables. (Notez que à cause d'un petit golf de laV
fonction d'aide , il ne générer des noms contenanta
pari
.)E::E(int d, std::map<std::string, int> m, char*&s)
analyse une chaîne d'entrées
en une expression AST basée sur un mappagem
des noms de variable actuellement liés en indices lambda-depth.f
est la fonction principale qui répond à la question.(Comme vous pouvez le constater sur le lien TIO, le code gère les noms de variables avec plusieurs caractères et obtient également une réponse correcte de
(\ a. (\ b. a))
pour((\ f. (\ x. (f x))) (\ y. (\ x. y)))
. Il arrive également que le code d'analyse puisse gérer l'ombrage des variables sans coût supplémentaire.)-16 octets en partie due à idée par ceilingcat (que j'étais venu aussi avec indépendamment), et en partie en raison du changement
E*a=new E;
deE&a=*new E;
puis changera->
àa.
-8 octets supplémentaires dus à un autre commentaire de ceilingcat (factor out assignation
a.t
from from ternary)-27 octets provenant de la conversion de l’analyseur et du clone en constructeurs de
E
la source
C 637 octets
Cette version n'utilise pas de variables auxiliaires (donc cela ne suit pas à 100% ce que dit le lambda calcul ... comme beaucoup d'autres ici ...). Chaque variable doit avoir une longueur de 1 caractère (comme certaines autres ici). Code de test:
résultats:
c'est le semi-ungolf:
la source