Générateur de calcul lambda

10

Je ne sais pas où poser cette question, j'espère que c'est un bon endroit.

Je suis juste curieux de savoir s'il est possible de faire un générateur de calcul lambda; essentiellement, une boucle qui, avec un temps infini, produira toutes les fonctions de calcul lambda possibles. (comme sous la forme d'une chaîne).

Étant donné que le calcul lambda est si simple, n'ayant que quelques éléments dans sa notation, j'ai pensé qu'il pourrait être possible (mais pas très utile) de produire toutes les combinaisons possibles de ces éléments de notation, en commençant par les combinaisons les plus simples, et ainsi de produire chaque lambda possible fonction de calcul.

Bien sûr, je ne sais presque rien du calcul lambda, donc je ne sais pas si c'est vraiment possible.

C'est ça? Si oui, est-ce assez simple comme je l'ai envisagé, ou est-ce techniquement possible, mais si difficile qu'il est effectivement impossible?

PS. Je ne parle pas des fonctions bêta réduites, je parle juste de chaque notation valide de chaque fonction de calcul lambda.

Legit Stack
la source

Réponses:

19

Bien sûr, il s'agit d'un exercice d'encodage standard.

Tout d'abord, supposons que toute fonction calculable bijective, appelée fonction d'appariement. Un choix standard estp:N2N

p(n,m)=(n+m)(n+m+1)2+n

On peut prouver qu'il s'agit d'une bijection, donc étant donné tout naturel , nous pouvons calculer n , m tel que p ( n , m ) = k .kn,mp(n,m)=k

Pour énumérer les termes lambda, corrigez toute énumération pour les noms de variables: .x0,x1,x2,

Ensuite, pour chaque nombre naturel , imprimer l a m b d a ( i ) , défini récursivement comme suit:ilambda(i)

  • si est pair, soit j = i / 2 et retourne la variable x jij=i/2xj
  • si est impair, soit j = ( i - 1 ) / 2jej=(i1)/2
    • si est pair, soit k = j / 2 et trouver n , m tel que p ( n , m ) = k ; calculer N = l a m b d a ( n ) , M = l a m b d a ( m ) ; demande de retour ( N M )jk=j/2n,mp(n,m)=kN=lambda(n),M=lambda(m)(NM)
    • si est impair, soit k = ( j - 1 ) / 2 et trouvons n , m tels que p ( n , m ) = k ; calculer M = l a m b d a ( m ) ; abstraction de retour ( λ x n . M )jk=(j-1)/2n,mp(n,m)=kM=lunembune(m)(λXn. M)

Ce programme est justifié par la bijection "algébrique" suivante impliquant l'ensemble de tous les termes lambda :Λ

ΛN+(Λ2+N×Λ)

qui se lit comme "les termes lambda, syntaxiquement, sont l'union disjointe de 1) variables (représentées comme un naturel), 2) applications (faites par deux termes lambda), et 3) abstraction (une variable paire / naturel + terme lambda ) ".

Étant donné que, nous appliquons récursivement les bijections calculables ( p ) et N + NN (la norme paire / impaire) pour obtenir l'algorithme ci-dessus.N2NpN+NN

Cette procédure est générale et fonctionnera sur presque tous les langages générés par une grammaire sans contexte, qui fournira un isomorphisme similaire à celui ci-dessus.

chi
la source
Wow, merci, est-il possible que vous puissiez représenter ceci est un pseudo code? Je comprendrais certainement mieux car je n'ai pas de diplôme cs.
Legit Stack
3
@LegitStack Eh bien, ce qui précède est un pseudo-code :) Vous pouvez définir une fonction récursive puis l'utiliser . La seule étape non triviale consiste à trouver n , m tel que p ( n , m ) = k : cela peut être fait en essayant toutes les paires n , m avec n , m k (des algorithmes plus rapides existent également)lunembune(n)if n%2==0 ...n,mp(n,m)=kn,mn,mk
chi
1
En effet, selon Wikipedia, l'inverse de cette fonction d'appariement particulière peut être trouvée via . une=12(8k+1-1),b=12une(une+1),n=b-k,m=une-n
LegionMammal978
12

Oui. Prenez quelque chose qui énumère toutes les chaînes ASCII possibles. Pour chaque sortie, vérifiez s'il s'agit d'une syntaxe de calcul lambda valide qui définit une fonction; sinon, sautez-le. (Cette vérification peut être effectuée.) Cela énumère toutes les fonctions de calcul lambda.

DW
la source
5
Essentiellement, tous les problèmes comme celui-ci sont résolus en invoquant le singe de frappe ...
xuq01
5
Ou vous pouvez énumérer directement les termes de calcul lambda. Beaucoup plus rapide que les chaînes aléatoires car chaque sortie est un terme correctement formaté. Ce serait comme remplacer les singes tapant par un générateur de jeu Shakespeare.
Dan D.
11

Comme cela a été mentionné, il s'agit simplement d'énumérer les termes d'un langage sans contexte, donc certainement faisable. Mais il y a des mathématiques plus intéressantes derrière cela, allant dans le domaine de la combinatoire analytique.

L'article Compter et générer des termes dans le calcul lambda binaire contient un traitement du problème d'énumération, et bien plus encore. Pour simplifier les choses, ils utilisent quelque chose appelé le calulus lambda binaire , qui est juste un encodage de termes lambda utilisant des indices De Bruijn , vous n'avez donc pas à nommer de variables.

Ce document contient également du code Haskell concret mettant en œuvre leur algorithme de génération. C'est certainement effectivement possible.

Il se trouve que j'ai écrit une implémentation de leur approche dans Julia.

phipsgabler
la source
7

Sûr. Nous pouvons les générer directement selon la définition des termes lambda.

Dans Haskell, nous définissons d'abord le type de données,

data LC a  =  Var  a                -- Constructor <type> <type> ...
           |  App (LC a) (LC a)     --
           |  Lam  a     (LC a)     --  ... alternatives ...

instance Show a => Show (LC a)      -- `LC a` is in Show if `a` is in Show, and
  where
    show (Var i)    =  [c | c <- show i, c /= '\'']
    show (App m n)  =  "("  ++ show m       ++ " " ++ show n ++ ")"
    show (Lam v b)  =  "(^" ++ show (Var v) ++ "." ++ show b ++ ")"

puis avec l'utilisation d'une foire (er) join,

lambda :: [a] -> [LC a]
lambda vars  =  terms 
  where
  terms  =  fjoin [ map Var vars ,
                    fjoin [ [App t s | t <- terms] | s <- terms ] ,
                    fjoin [ [Lam v s | v <- vars ] | s <- terms ] ]

  fjoin :: [[a]] -> [a]
  fjoin xs  =  go [] xs             -- fairer join
      where 
      go [] []  =  []
      go a  b   =  reverse (concatMap (take 1) a) ++ go 
                       (take 1 b ++ [t | (_:t) <- a]) (drop 1 b)

nous les énumérons, comme par exemple

> take 20 $ lambda "xyz"
[x,y,(x x),z,(y x),(^x.x),(x y),(^y.x),((x x) x),(^x.y),(y y),(^z.x),(x (x x)),
 (^y.y),(z x),(^x.(x x)),((x x) y),(^z.y),(y (x x)),(^y.(x x))]

> take 5 $ drop 960 $ lambda "xyz"
[(((x x) y) (z x)),(^y.(^x.((x x) (x x)))),((^x.(x x)) (^x.(x x))),(^x.((^z.x) 
 y)),((z x) ((x x) y))]

Ω=(λX.XX)(λX.XX)

fjoinest équivalent à Omega monade s » diagonal.

Will Ness
la source
0

Je suis tombé sur un outil en ligne qui peut générer des exemples de chaînes à partir d'une expression régulière: https://www.browserling.com/tools/text-from-regex . Vous pouvez générer de nombreux exemples de termes lambda en saisissant quelque chose comme ceci:

(\( (lambda \w\. )* \w+ \))* 

Bien sûr, pour obtenir des termes avec des niveaux d'imbrication arbitraires, vous devrez utiliser une grammaire sans contexte, qui est un outil plus descriptif pour définir une langue qu'une expression régulière. Je n'ai pas trouvé d'outil existant pour générer des exemples de phrases de langage basées sur une définition de grammaire sans contexte, mais il n'y a aucune raison pour laquelle il n'a pas pu être construit.

Martin
la source
2
λ