型なしラムダ計算のインタープリターを作成する


45

課題は、型付けされていないラムダ計算のインタプリタをできるだけ少ない文字で記述することです。型なしラムダ計算を次のように定義します。

構文

次の3種類の式があります。

  • ラムダ式は、フォームがあるいかなる法的な変数名となる可能性が法的表現を。ここはパラメータと呼ばれ、関数本体と呼ばれます。(λ x. e)xexe

    簡単にするために、x現在スコープ内にあるのと同じ名前の変数があってはならないという制限を追加します。変数は、その名の間に表示されたときに範囲にあることが始まり.し、対応するの範囲であることを停止します)

  • 関数適用の形式は(f a)where fおよびaareの正規表現です。ここfは関数とa呼ばれ、引数と呼ばれます。
  • 変数は、フォームがある法的な変数名です。xx

意味論

関数は、関数本体内のパラメーターの各オカレンスを引数で置き換えることにより適用されます。より正式フォームの発現((λ x. e) a)x変数名、eおよびa式に表現、評価する(又は減少)しているの各発生を交換した結果であるに有します。e'e'xea

正規形は、それ以上評価できない式です。

挑戦

あなたの使命は、それを受け入れることを選択した場合、自由変数を含まない型なしラムダ計算の入力を入力として受け取り、その出力として式の正規形(またはそれに一致する式)を生成するインタープリターを書くことです。式に正規形がないか、有効な式でない場合、動作は未定義です。

文字数が最小のソリューションが優先されます。

いくつかのメモ:

  • 入力は、stdinまたはコマンドライン引数として指定されたファイル名から読み取ることができます(両方ではなく、どちらか一方のみを実装する必要があります)。出力は標準出力に送られます。
  • または、入力を文字列として受け取り、出力を文字列として返す関数を定義できます。
  • 非ASCII文字に問題がある場合は、\λの代わりにバックスラッシュ()文字を使用できます。
  • バイトではなく文字数をカウントするため、ソースファイルがUnicodeとしてエンコードされている場合でも、λは1文字としてカウントされます。
  • 有効な変数名は1つ以上の小文字、つまりaとzの間の文字で構成されます(英数字名、大文字、または非ラテン文字をサポートする必要はありません-もちろん、そうすることでソリューションが無効になることはありません)。
  • この課題に関する限り、オプションの括弧はありません。各ラムダ式と各関数アプリケーションは、ちょうど1組の括弧で囲まれます。変数名は括弧で囲まれません。
  • 書き込みなどの糖衣構文(λ x y. e)のためには(λ x. (λ y. e))、サポートする必要はありません。
  • 関数を評価するために100を超える再帰の深さが必要な場合、動作は未定義です。これは、すべての言語で最適化せずに実装するのに十分なほど低く、ほとんどの式を実行できるほど十分に大きい必要があります。
  • また、無入力の開始時と終了時または前にスペースすなわち、その間隔は例のようになりますと仮定しないかもしれないλか、.1つだけ空白の後.と関数とその引数の間と後λ

サンプルの入力と出力

  • 入力: ((λ x. x) (λ y. (λ z. z)))

    出力: (λ y. (λ z. z))

  • 入力: (λ x. ((λ y. y) x))

    出力: (λ x. x)

  • 入力: ((λ x. (λ y. x)) (λ a. a))

    出力: (λ y. (λ a. a))

  • 入力: (((λ x. (λ y. x)) (λ a. a)) (λ b. b))

    出力: (λ a. a)

  • 入力: ((λ x. (λ y. y)) (λ a. a))

    出力: (λ y. y)

  • 入力: (((λ x. (λ y. y)) (λ a. a)) (λ b. b))

    出力: (λ b. b)

  • 入力: ((λx. (x x)) (λx. (x x)))

    出力:何でも(これは通常の形式を持たない式の例です)

  • 入力: (((λ x. (λ y. x)) (λ a. a)) ((λx. (x x)) (λx. (x x))))

    出力:((λ a. a)これは、関数呼び出しの前に引数を評価した場合に正規化されない式の例であり、悲しいことに私の試みた解決策が失敗する例です)

  • 入力: ((λ a. (λ b. (a (a (a b))))) (λ c. (λ d. (c (c d)))))

    出力:`(λ a. (λ b. (a (a (a (a (a (a (a (a b)))))))))) 教会の数字で2 ^ 3を計算します。


1
文字列の前後に空白が追加されたり、空白がサンプル入力で指定されていると仮定したりできますか?つまり、角かっこの間、ドットとパラメータ名の間、および空白の他のインスタンスとの間の空白は正確に1スペースではありません。
-JPvdMerwe

@JPvdMerwe:はい、良い点、あなたはそれを仮定するかもしれません。
sepp2k

無料の変数はありますか?式のように、ラムダに束縛されていない変数を意味します(\y. a)
-FUZxxl

3
ここでのソリューションの多くまたはすべては、キャプチャ回避置換を実装できません!((λf。(λx。(fx)))(λy。(λx。y)))のようなテストケースを追加する必要があります。このテストケースは(λx。(λz。x))、 not(λx。(λx。x))。
アンデルスカセオルグ

1
@ sepp2kテストケースとして((λf。(λx。(fx)))(λy。(λx。y)))を追加し、(λx。(λ x。x))?
アンデルスカセオルグ

回答:


36

最新:

私はそれを下に絞ってきた644の文字私はコピーして、パーへの細胞の一部を織り込み、。cellおよびcdrの呼び出しを一時的なローカル変数にキャッシュし、それらのローカル変数を「ターミナル」(つまり、非再帰)関数のグローバルに移動しました。また、10進定数は文字リテラルよりも短く、この厄介なビジネス...

atom(x){
    return m[x]>>5==3;
}

...小文字(ASCIIを想定)を正しく識別しますが、任意の `{|}〜も受け入れます。(ASCIIに関するこの同じ観察結果は、UTF-8に関するこの優れたビデオで作成されています。)

Et viola:|

#include<stdio.h>
#include<string.h>
#define X m[x]
#define R return
char*n,*m;int u,w,d;C(x,y){w=n-m;n+=sprintf(n,y?"(%s %s)":"(%s)",&X,m+y)+1;R w;}T(x){R X>>5==3;}
L(x){R X==92;}O(x,j){w=n-m;memcpy(n,&X,j);n+=j;*n++=0;R w;}E(x){X==' '?++x:0;R
X==41?0:L(x)?O(x,4):P(x);}P(x){d=0,w=x;do{X==40?d++:X==41?d--:0;++x;}while(d>0);R
O(w,x-w);}D(x){u=E(x+1);R u?E(x+1+strlen(m+u)):0;}V(x){int a=E(x+1),b=D(x);R
T(x)|T(a)?x:L(a)?C(a,V(b)):L(E(a+1))?V(S(V(b),E(a+3),D(a))):V(C(V(a),b?V(b):0));}S(w,y,x){R
T(x)?(X==m[y]?w:x):C(L(w+1)?E(x+1):S(w,y,E(x+1)),D(x)?S(w,y,D(x)):0);}
Y(char*s){n+=strlen(s=strcpy(n,s))+1;printf("%s\n%s\n\n",s,m+V(s-m));n=m+1;}

char*s[]={
"((\\ a. a) (b))",
"((\\ x. x) (\\ y. (\\ z. z)))",
"(\\ x. ((\\ y. y) x))",
"(((\\ x. (\\ y. x)) (\\ a. a)) (\\ b. b))",
"((\\ x. (\\ y. y)) (\\ a. a))",
"(((\\ x. (\\ y. y)) (\\ a. a)) (\\ b. b))",
"((\\x. (x x)) (\\x. (x x)))",0};
#include<unistd.h>
main(){char**k;n=m=sbrk(4096);*n++=0;for(k=s;*k;k++)Y(*k);R 0;}

以前:

努力して数票獲得できますか?私は一週間この昼と夜に取り組んでいます。オリジナルのMcCarthy論文を掘り出し、Paul GrahamのThe Roots of Lispの付録を読むまで、論文自体のバグに悩まされていました。私は気が散り、家を閉め出してから、その夜の12時30分に再び家に着くまで完全に忘れてしまいました(少し遅れて郡内に住んでいるビルのマネージャーに電話することになりました)。祖母の夜(ラップトップのバッテリーが乾くまでハッキングして)。

そして、結局のところ、それは優勝したエントリーの近くにさえありません!

これをもっと短くする方法がわかりません。そして、私は考えることができるすべての汚いトリックを使用しました!たぶん、Cではできません。

カウントに多少の寛大さ(最初のチャンクは文字列を取り、結果を出力します)では、778 770 709 694文字です。しかし、スタンドアロンにするには、そのsbrk呼び出しが必要です。また、より複雑な式を処理するには、signalハンドラーも必要です。そしてもちろん、それを使用しようとするコードでモジュールにすることはできませんmalloc

悲しいかな、ここにあります:

#include<stdio.h>
#include<string.h>
#define K(j) strncpy(n,m+x,j);n+=j;goto N;
#define R return
#define X m[x]
#define L =='\\'
char*m,*n;T(x){R islower(X);}V(x){int a=E(x+1);R
T(x)?x:T(a)?x:m[a]L?C(a,V(D(x))):m[E(a+1)]L?V(S(V(D(x)),E(a+3),D(a))):V(C(V(a),D(x)?V(D(x)):0));}
C(x,y){char*t=n;sprintf(n,y?"(%s %s)":"(%s)",m+x,m+y);n+=strlen(n)+1;R
t-m;}Y(char*s){char*t=strcpy(n,s);n+=strlen(n)+1;printf("%s=>%s\n",s,m+V(t-m));n=m+1;}S(x,y,z){R
T(z)?(m[z]==m[y]?x:z):C(m[z+1]L?E(z+1):S(x,y,E(z+1)),D(z)?S(x,y,D(z)):0);}D(x){R
E(x+1)?E(x+strlen(m+E(x+1))+1):0;}E(x){char*t=n,d=0;if(X==' ')++x;if(T(x)){K(1)}if(X
L){K(4)}do{d=X?(X=='('?d+1:(X==')'?d-1:d)):0;*n++=m[x++];}while(d);N:*n++=0;R t-m;}

char*samp[]={
    "a","a","b","b",
    "((\\ a. a) (b))", "(b)",
    "((\\ x. x) (\\ y. (\\ z. z)))", "(\\ y. (\\ z. z))",
    "(\\ x. ((\\ y. y) x))", "(\\ x. x)",
    "(((\\ x. (\\ y. x)) (\\ a. a)) (\\ b. b))", "(\\ a. a)",
    "((\\ x. (\\ y. y)) (\\ a. a))", "(\\ y. y)",
    "(((\\ x. (\\ y. y)) (\\ a. a)) (\\ b. b))", "(\\ b. b)",
    "((\\x. (x x)) (\\x. (x x)))", "undef",
    NULL};
#include<unistd.h>

unsigned sz;
#include<signal.h>
void fix(x){signal(SIGSEGV,fix);brk(m+(sz*=2));}
main(){
    char**t;
    signal(SIGSEGV,fix);
    m=n=sbrk(sz=10*getpagesize());
    *n++=0;
    for(t=samp;*t;t+=2){
        Y(*t);
        printf("s.b. => %s\n\n", t[1]);
    }
    return 0;
}

これが最終的な削減の直前のブロックです。ここでのコツは、ポインターではなく整数カーソル(「暗黙のint」動作を利用)、および「スクラッチメモリ」の使用です。これchar*nは、空き領域への「新しい」または「次の」ポインターです。しかし、文字列をメモリに書き込み、strlenを呼び出してnをインクリメントすることがあります。サイズの計算が容易になった後、メモリを効果的に使用してから割り当てます。cell()関数とデータの文字列表現の間のインターフェイスを除いて、McCarthyの論文からほとんどまっすぐであることがわかります。

#include<stdio.h>
#include<string.h>
char*m,*n;  //memory_base, memory_next
atom(x){  // x is an atom if it is a cursor to a lowercase alpha char.
    return x?(islower(m[x])?m[x]:0):0;
}
eq(x,y){  // x and y are equal if they are both atoms, the same atom.
    return x&&y&&atom(x)==atom(y);
}
cell(x){  // return a copy of the list-string by cursor, by parsing
    char*t=n,d=0;
    if(!x||!m[x])
        return 0;
    if(m[x]==' ')
        ++x;
    if(atom(x)){
        *n++=m[x];
        *n++=0;
        return(n-m)-2;
    }
    if(m[x]=='\\'){  // our lambda symbol
        memcpy(n,m+x,4);
        n+=4;
        *n++=0;
        return(n-m)-5;
    }
    do{  // um ...
        d=m[x]?(m[x]=='('?d+1:(m[x]==')'?d-1:d)):0;
        *n++=m[x++];
    }while(d);
    *n++=0;
    return t-m;
}
car(x){  // return (copy of) first element
    return x?cell(x+1):0;
}
cdr(x){  // return (copy of) rest of list
    return car(x)?cell(x+strlen(m+car(x))+1):0;
}
cons(x,y){  // return new list containing first x and rest y
    char*t=n;
    return x?(sprintf(n,y?"(%s %s)":"(%s)",m+x,m+y),n+=strlen(n)+1,t-m):0;
}
subst(x,y,z){  // substitute x for z in y
    if(!x||!y||!z)
        return 0;
    return atom(z)? (eq(z,y)?x:z):
        cons(m[z+1]=='\\'?car(z):
        subst(x,y,car(z)),cdr(z)?subst(x,y,cdr(z)):0);
}
eval(x){  // evaluate a lambda expression
    int a;
    return atom(x)?x:
        atom(a=car(x))?x:
        m[a]=='\\'?cons(a,eval(cdr(x))):
        m[car(a)]=='\\'?eval(subst(eval(cdr(x)),cell(a+3),cdr(a))):
        eval( cons(eval(a),cdr(x)?eval(cdr(x)):0));
}
try(char*s){  // handler
    char*t=strcpy(n,s);
    n+=strlen(n)+1;
    printf("input: %s\n", s);
    printf("eval => %s\n", m+eval(t-m));
    n=m+1;
}

1
キャラクターを1つまたは2つ救うために、さらにいくつかのトリックを見つけましたが、急進的なものはありません。sprintf(n,...);n+=strlen(n)+1;優れているn+=sprintf(n,...)+1;配列構文を反転x[m]代わりにm[x]、私は「postfixの」マクロですべての間接を交換することができます#define M [m]... x M1つの文字を保存し、トークンを分離する必要があるので、空白「自由」改行を与えます。
luser droog

これとIOCCC 1989の jar.2 xlisp 4.0にはいくつかの類似点があるようです。
luser droog

私はこれをより充実したLispインタプリタに拡張しようとしました。
luserはドローグ

コメントされたコードは// um ...、文字列をループし、適切なネストレベルで一致するかっこが見つかるまで括弧をカウントします。
luser droog

1
これは、((\ f。(\ x。(fx)))(\ y。(\ x。y)))を(\ x。(fx))に誤って評価します。
アンデルスカセオルグ

22

バイナリラムダ計算186

以下の16進ダンプに示されているプログラム

00000000  18 18 18 18 18 18 44 45  1a 10 18 18 45 7f fb cf  |......DE....E...|
00000010  f0 b9 fe 00 78 7f 0b 6f  cf f8 7f c0 0b 9f de 7e  |....x..o.......~|
00000020  f2 cf e1 b0 bf e1 ff 0e  6f 79 ff d3 40 f3 a4 46  |........oy..@..F|
00000030  87 34 0a a8 d0 80 2b 0b  ff 78 16 ff fe 16 fc 2d  |.4....+..x.....-|
00000040  ff ff fc ab ff 06 55 1a  00 58 57 ef 81 15 bf bf  |......U..XW.....|
00000050  0b 6f 02 fd 60 7e 16 f7  3d 11 7f 3f 00 df fb c0  |.o..`~..=..?....|
00000060  bf f9 7e f8 85 5f e0 60  df 70 b7 ff ff e5 5f f0  |..~.._.`.p...._.|
00000070  30 30 6f dd 80 5b b3 41  be 85 bf ff ca a3 42 0a  |00o..[.A......B.|
00000080  c2 bc c0 37 83 00 c0 3c  2b ff 9f f5 10 22 bc 03  |...7...<+...."..|
00000090  3d f0 71 95 f6 57 d0 60  18 05 df ef c0 30 0b bf  |=.q..W.`.....0..|
000000a0  7f 01 9a c1 70 2e 80 5b  ff e7 c2 df fe e1 15 55  |....p..[.......U|
000000b0  75 55 41 82 0a 20 28 29  5c 61                    |uUA.. ()\a|
000000ba

あなたが提案する形式をまったく受け入れません。むしろ、バイナリラムダ計算(blc)形式のラムダ項を想定しています。ただし、最小の括弧を使用して、通常のフォームの縮小のすべてのステップを示しています。

例:教会の数字で2 ^ 3を計算する

上記の16進ダンプをxxd -r> symbolic.Blcで保存します

http://tromp.github.io/cl/uni.cからblcインタープリターを入手します

cc -O2 -DM=0x100000 -m32 -std=c99 uni.c -o uni
echo -n "010000011100111001110100000011100111010" > threetwo.blc
cat symbolic.Blc threetwo.blc | ./uni
(\a \b a (a (a b))) (\a \b a (a b))
\a (\b \c b (b c)) ((\b \c b (b c)) ((\b \c b (b c)) a))
\a \b (\c \d c (c d)) ((\c \d c (c d)) a) ((\c \d c (c d)) ((\c \d c (c d)) a) b)
\a \b (\c (\d \e d (d e)) a ((\d \e d (d e)) a c)) ((\c \d c (c d)) ((\c \d c (c d)) a) b)
\a \b (\c \d c (c d)) a ((\c \d c (c d)) a ((\c \d c (c d)) ((\c \d c (c d)) a) b))
\a \b (\c a (a c)) ((\c \d c (c d)) a ((\c \d c (c d)) ((\c \d c (c d)) a) b))
\a \b a (a ((\c \d c (c d)) a ((\c \d c (c d)) ((\c \d c (c d)) a) b)))
\a \b a (a ((\c a (a c)) ((\c \d c (c d)) ((\c \d c (c d)) a) b)))
\a \b a (a (a (a ((\c \d c (c d)) ((\c \d c (c d)) a) b))))
\a \b a (a (a (a ((\c (\d \e d (d e)) a ((\d \e d (d e)) a c)) b))))
\a \b a (a (a (a ((\c \d c (c d)) a ((\c \d c (c d)) a b)))))
\a \b a (a (a (a ((\c a (a c)) ((\c \d c (c d)) a b)))))
\a \b a (a (a (a (a (a ((\c \d c (c d)) a b))))))
\a \b a (a (a (a (a (a ((\c a (a c)) b))))))
\a \b a (a (a (a (a (a (a (a b)))))))

hexdumpはかなり読みにくいため、ここに「逆アセンブル」バージョンがあります

@10\\@10\\@10\\@10\\@10\\@10\@\@\@\@@\@1010\@\\\@10\\@10\@\@@@1111111111101
1110@11111110\@@110@11111110\\\\@1110\@1111110\@@101101111110@111111110\@111
111110\\\\@@110@111111011110@11111011110@@10@1111110\@10110\@@111111110\@111
111110\@110@101111011110@1111111111010@1010\\@1110@11010@\@\@1010\@110@1010\
\@@@@@\@1010\@\\\\@@@10\@@111111111011110\\@@101111111111111110\@@101111110\
@@10111111111111111111111110@@@@1111111110\\110@@@@\@1010\\\\@@10\@@@1111101
11110\\@\@@@10111111101111110\@@1011011110\\@@11111010110\\@111110\@@1011110
1110@111010\10\1011111110@111110\\\@101111111111011110\\@@11111111110@@11111
0111110\10\@@@@11111110\\@10\\1101111101110\@@1011111111111111111111110@@@@1
11111110\\@10\\@10\\11011111101110110\\\@@101110110@1010\\11011111010\@@1011
111111111111110@@@@\@1010\@\\@@@10\@@@1110@10\\\@1011110\\110\\\@10\\\@1110\
@@@11111111110@1111111101010\10\\@\@@@1110\\\@10@1110111110\\1110\110@@@1111
0110@@@1111010\\110\\\@10\\\@@1101111111101111110\\\@10\\\@@1101111110111111
10\\\110@1010110\\101110\\@@11010\\\@@1011111111111110@11110\@@1011111111111
101110\@\@@@@@@@@11010101010101010\\110\\10\\1010\10\\\1010\\1010@@@110\110\
@

00(ラムダ)を\に、01(アプリケーション)を@に置き換えると、brainfuckとほぼ同じくらい読みやすくなります:-)

http://www.ioccc.org/2012/tromp/hint.htmlも参照してください


7
BLCはたまたまバイナリアルファベットを使用しています。00はラムダ、01はアプリケーション、1 ^ {n} 0は単項変数です。コンパイルは必要ありません。
ジョントロンプ

3
ファクターx3はどこで得られますか?実際、BFのような小さなソースアルファベットの言語は罰せられるという点で良い点を挙げます。公正な比較のために、すべてのサイズはビットで表現される必要があり、BF文字はそれぞれ3ビットのみを取ります。他のほとんどの言語は、いくつかは、すべて8.使用、ASCIIのための7ビットを必要とする
ジョンTromp

1
ところで+1
luserはドローグ

1
フラクトランでのフラクトランが許容できる場合、なぜこれが問題になるべきかはわかりません。読めないの?あなたはしたい?学ぶ!
luser droog

1
実際の入力形式を読み取るには何が必要ですか?それがあなたが潜在的な賛成票を失っているところだと思います。
luser droog

14

ハスケル、342の 323 317 305文字

これを書いている時点で、これは((λf。(λx。(fx)))(λy。(λx。y)))を正しい結果(λx。(λz。 x))(λx。(λx。x))ではなく。ラムダ計算の適切な実装には、この問題のスコープ内の別の変数をシャドウイングしない変数の単純化保証の下でも、キャプチャ回避置換が必要です。(私のプログラムは、この保証がなくても機能します。)

data T=T{a::T->T,(%)::ShowS}
i d=T(i. \x v->'(':d v++' ':x%v++")")d
l f=f`T`\v->"(λ "++v++". "++f(i(\_->v))%('x':v)++")"
(?)=q.lex
q[(v,s)]k|v/="("=k(maybe T{}id.lookup v)s|'λ':u<-s,[(w,_:t)]<-lex u=t? \b->k(\e->l$b.(:e).(,)w).tail|0<1=s? \f->(?(.tail).k. \x z->f z`a`x z)
main=interact(? \f->(f[]%"x"++))

ノート:

  • このチャレンジは2011年1月に設定されたため、必要に応じてGHC 7.0で実行されます。GHC7.10を想定できる場合、13文字短くなります。

ドキュメント付きのゴルフ版ではありません


ideone haskellコンパイラの入力は、((\ x。x)(\ y。(\ z。z)))へのコンパイラで、((\\ x。x)(\\ y。( \\ z。z)))... Haskellで「lex」とはどういう意味ですか?
RosLuP

2
@RosLuP私のプログラムは\ではなくλを受け入れます。
アンデルスカセオルグ

ideone.comにこの入力((λx。x)(λy。(λz。z)))を入力します戻り値:ランタイムエラー時間:0メモリ:4876信号:-1
RosLuP

1
@RosLuP IdeoneはUnicodeのサポートが壊れているようです。コマンドラインまたは別のオンラインインタープリターを試してください(たとえば、Rextesterで動作します)。
アンデルスカセオルグ

2
@codeshot質問の著者は、((λf 。(λx。(fx)))(λy 。(λx。y )))↦(λx。(λz。x))が正しいことを既にコメントしていますこの問題(実際のラムダ計算と同様)。
アンデルスカセオルグ

13

パイソン-321 320

これが私の(修正された)試みです:

l="("
def S(s):
 if s[0]!=l:return s
 if s[1]=="\\":g=s.find('.');return"(\\ %s. %s)"%(s[3:g],S(s[g+2:-1]))
 i=2;c=s[1]==l
 while c:c+=(s[i]==l)-(s[i]==')');i+=1
 t=S(s[1:i])
 z=s[i+1:-1]
 if l!=t[0]:return"(%s %s)"%(t,S(z))
 g=t.find('.')
 t=S(t[g+2:-1]).replace(t[3:g],z)
 if t!=s:t=S(t)
 return t
print S(raw_input())

これは良さそうに見えますが、機能していないようです。いくつかのサンプルの入力と出力を追加しましたが、コードは間違った結果を生成します。
sepp2k

1
これは、キャプチャ回避置換を実行できません。たとえば、((\ f。(\ x。(fx)))(\ y。(\ x。y)))は、(\ x。(\ x。x))と誤って評価されます。
アンデルスカセオルグ

1
それがほとんど機能していないのに、なぜこれが答えとしてマークされているのですか?著者によって与えられた入力と出力を試しましたか?
rbaleksandar

1
著者が提供したテストケースは、この回答のバグを実証するには不十分です。
アンデルスカセオルグ16

1
この答えは正確でも最短でもありません。キャプチャを回避できず、文字列置換のバグがあります。
リチャードパドリー

6

ルビー254文字

f=->u,r{r.chars.take_while{|c|u+=c==?(?1:c==?)?-1:0;u>0}*''}
l=->x{x=~/^(\(*)\(\\ (\w+)\. (.*)/&&(b,v,r=$1,$2,$3;e=f[1,r];(e==s=l[e])?b==''?x:(s=f[2,r];(x==y=b.chop+e.gsub(v,s[2+e.size..-1])+r[1+s.size..-1])?x:l[y]):(b+'(\\ '+v+'. '+s+r[e.size..-1]))||x}

次のように使用できます

puts l["((\\ x. (\\ y. x)) (\\ a. a))"]    # <= (\ y. (\ a. a))

解決策はまだ完全にゴルフされていませんが、すでにほとんど読めません。


こんにちは
en望

これは、キャプチャ回避置換を実行できません。たとえば、((\ f。(\ x。(fx)))(\ y。(\ x。y)))は、(\ x。(\ x。x))と誤って評価されます。
アンデルスカセオルグ

上記のキャプチャバグに加えて、これは(\ y。(\ xx。((\ x。xx)y)))から(\ y。(\ xx。yy))への誤った評価も行います。存在しない変数yy。
アンデルスカセオルグ

3

編集:純粋なJavaScriptの下で250の私の答えを確認してください。

LiveScriptを使用した2852 243文字(正規表現なし!完全にゴルフではない-改善可能)

L=(.0==\\)
A=->it.forEach?&&it.0!=\\
V=(.toFixed?)
S=(a,b,t=-1,l=0)->|L a=>[\\,S(a.1,b,t,l+1)];|A a=>(map (->S(a[it],b,t,l)),[0 1]);|a==l+-1=>S(b,0,l+-1,0)||a|l-1<a=>a+t;|_=>a
R=(a)->|L a=>[\\,R a.1]|(A a)&&(L a.0)=>R(S(R(a.0),R(a.1)).1)|_=>a

テスト:

a = [\\,[\\,[1 [1 0]]]]
b = [\\,[\\,[1 [1 [1 0]]]]]
console.log R [a, b]
# outputs ["\\",["\\",[1,[1,[1,[1,[1,[1,[1,[1,[1,0]]]]]]]]]]]

どちらが3^2=9OPに述べたように、。

誰かが興味があるなら、ここにいくつかのコメント付きの拡張バージョンがあります:

# Just type checking
λ = 100
isλ = (.0==λ)
isA = -> it.forEach? && it.0!=λ
isV = (.toFixed?)

# Performs substitutions in trees
# a: trees to perform substitution in
# b: substitute bound variables by this, if != void
# f: add this value to all unbound variables
# l: internal (depth)
S = (a,b,t=-1,l=0) ->
    switch
    | isλ a             => [λ, (S a.1, b, t, l+1)]
    | isA a             => [(S a.0, b, t, l), (S a.1, b, t, l)]
    | a == l - 1        => (S b, 0, (l - 1), 0) || a
    | l - 1 < a < 100   => a + t
    | _                 => a

# Performs the beta-reduction
R = (a) ->
    switch
    | (isλ a)               => [λ,R a.1]
    | (isA a) && (isλ a.0)  => R(S(R(a.0),R(a.1)).1)
    | _                     => a

# Test
a = [λ,[λ,[1 [1 0]]]]
b = [λ,[λ,[1 [1 [1 0]]]]]
console.log show R [a, b]

これは、問題の入出力仕様に準拠していません。
アンデルスカセオルグ

3

ウォーターハウスアーク-140文字

(=
f[is cons?&car._'λ]n[if
atom._ _
f._ `(λ,_.1,n:_.2)(=
c n:_.0
e _)(if
f.c(n:deep-map[if(is
c.1 _)e.1
_]c.2)(map n
_))]λ[n:read:rem #\._])

Waterhouse Arcはどこで入手できますか?
アンデルスカセオルグ

1
インタプリタがどこにも見つからではないとして無効

@AndersKaseorg ここに
ASCIIのみの

@ASCIIのみArcが何であるかは知っていますが、「Waterhouse」の部分から、特定の方言が必要であることが示唆されました。実行してもらいましたか?
アンデルスカセオルグ

@AndersKaseorg気にしないで それを見つけた
ASCIIのみの

2

C 1039バイト

#define F for
#define R return
#define E if(i>=M||j>=M)R-1;
enum{O='(',C,M=3999};signed char Q[M],D[M],t[M],Z,v,*o=Q,*d=D,*T;int m,n,s,c,w,x,y;K(i,j,k){!Z&&(Z=t[O]=1)+(t[C]=-1);E;if(!o[i]){d[j]=0;R 0;}if((c=t[o[i]]+t[o[i+1]])!=2||o[i+2]!='\\'){d[j++]=o[i++];R K(i,j,i);}F(i+=2,y=w=0;i<M&&o[i]&&c;++i)c+=t[o[i]],!w&&c==1?w=i:0,!y&&o[i]=='.'?y=i+2:0;E;if(c){F(;d[j++]=o[i++];)E;R 0;}F(c=y;c<w;++c)if(o[c]=='\\')F(n=0,m=w+2;m<i;++m){if(o[m]==o[c+2]){F(x=0;o[m+x]&&isalpha(o[m+x])&&o[m+x]==o[c+2+x];++x);if(o[c+2+x]!='.'||isalpha(o[m+x]))continue;if(v>'Z')R-1;F(n=c+2;n<w;++n)if(o[n]==o[m]){F(x=0; o[m+x]&&isalpha(o[m+x])&&o[m+x]==o[n+x];++x);if(o[m+x]=='.'&&!isalpha(o[n+x]))F(;--x>=0;) o[n+x]=v;}++v;}}F(c=y;c<w&&j<M;++c){F(x=0;o[c+x]&&o[c+x]==o[k+4+x]&&isalpha(o[c+x]); ++x);if(o[k+4+x]=='.'&&!isalpha(o[c+x])){F(m=w+2;m<i-1&&j<M;++m)d[j++]=o[m];c+=x-1;}else d[j++]=o[c];}E;Z=2;R K(i,j,i);}char*L(char*a){F(s=n=0;n<M&&(o[n]=a[n]);++n);if(n==M)R 0;v='A';F(;++s<M;){Z=0;n=K(0,0,0);if(Z==2&&n!=-1)T=d,d=o,o=T;else break;}R n==-1||s>=M?0:d;}

変数は、小文字[a..zから]を使用して入力できるようにします。出力で必要な場合、sysは大文字[A..Zから]を使用して変数を生成できます。ASCII文字構成を想定します。

#define P printf
main()
{char  *r[]={ "((\\ abc. (\\ b. (abc (abc (abc b))))) (\\ cc. (\\ dd. (cc (cc dd)))))",
              "((\\ fa. (\\ abc. (fa abc))) (\\ yy. (\\ abc. yy)))",
              "((\\ x. x) z)", 
              "((\\ x. x) (\\ y. (\\ z. z)))", 
              "(\\ x. ((\\ y. y) x))", 
              "((\\ x. (\\ y. x)) (\\ a. a))", 
              "(((\\ x. (\\ y. x)) (\\ a. a)) (\\ b. b))",
              "((\\ x. (\\ y. y)) (\\ a. a))",
              "(((\\ x. (\\ y. y)) (\\ a. a)) (\\ b. b))",             
              "((\\ x. (x x)) (\\ x. (x x)))",
              "(((\\ x. (\\ y. x)) (\\ a. a)) ((\\ x. (x x)) (\\ x. (x x))))",
             0}, *p;
 int    w;

 for(w=0; r[w] ;++w)
   {p=L(r[w]);
    P("o=%s d=%s\n", r[w], p==0?"Error ":p);
   }
 R  0;
}

/*1.039*/

指定には、/ではなく、\またはλが必要です。また、複数文字の変数名のサポートも必要です。
アンデルスカセオルグ

「\ n」はなどの記号「\」ではなく「/」より有効に活用され、他の用途がある
RosLuP

1
それでも、課題は仕様を改善することではなく、仕様を満たすことです。
アンデルスカセオルグ

私はそれをもう少し適合させるために何かを書いた...しかし、サイズが爆発する
...-RosLuP


1

ハスケル456 C

Haskellの遅延評価機能が完全に利用されている場合は、はるかに短くなる可能性があります。悲しいことに、私はそれを行う方法がわかりません。

また、解析ステップで多くの文字が無駄になります。

data T=A[Char]|B[Char]T|C T T
(!)=(++)
s(A a)=a
s(B a b)="(λ "!a!". "!s b!")"
s(C a b)='(':s a!" "!s b!")"
e d(A a)=maybe(A a)id(lookup a d)
e d(B a b)=B a.e d$b
e d(C a b)=f d(e d a)(e d b)
f d(B x s)q=e((x,q):d)s
f d p q=C p q
d=tail
p('(':'λ':s)=let(A c,t)=p(d s);(b,u)=p(d.d$t);in(B c b,d u)
p('(':s)=let(a,t)=p s;(b,u)=p(d t)in(C a b,d u)
p(c:s)|elem c" .)"=(A "",c:s)|1<2=let((A w),t)=p s in(A(c:w),t)
r=s.e[].fst.p
main=do l<-getLine;putStrLn$r l

ゴルフされていないバージョン

data Expression = Literal String 
                | Lambda String Expression
                | Apply Expression Expression
                deriving Show

type Context = [(String, Expression)]

show' :: Expression -> String
show' (Literal a) = a
show' (Lambda x e) = "(λ " ++ x ++ ". " ++ show' e ++ ")"
show' (Apply e1 e2) = "(" ++ show' e1 ++ " " ++ show' e2 ++ ")"

eval :: Context -> Expression -> Expression
eval context e@(Literal a) = maybe e id (lookup a context)
eval context (Lambda x e) = Lambda x (eval context e)
eval context (Apply e1 e2) = apply context (eval context e1) (eval context e2)

apply :: Context -> Expression -> Expression -> Expression
apply context (Lambda x e) e2 = eval ((x, e2):context) e
apply context e1 e2 = Apply e1 e2

parse :: String -> (Expression, String)
parse ('(':'λ':s) = let
    (Literal a, s') = parse (tail s)
    (e, s'') = parse (drop 2 s')
    in (Lambda a e, tail s'')

parse ('(':s) = let
    (e1, s') = parse s
    (e2, s'') = parse (tail s')
    in (Apply e1 e2, tail s'')

parse (c:s) | elem c " .)" = (Literal "", c:s)
            | otherwise    = let ((Literal a), s') = parse s 
                             in (Literal (c:a), s')

run :: String -> String
run = show' . eval [] . fst . parse
main = do
  line <- getLine
  putStrLn$ run line

3
これは、キャプチャ回避置換を実行できません。たとえば、((λf。(λx。(fx)))(λy。(λx。y)))は、(λx。(λx。x))と誤って評価されます。
アンデルスカセオルグ

1

JavaScriptで231を得た/正規表現なし

(function f(a){return a[0]?(a=a.map(f),1===a[0][0]?f(function d(b,a,e,c){return b[0]?1===b[0]?[1,d(b[1],a,e,c+1)]:2===b[0]?b[1]===c-1?d(a,0,c-1,0)||b:c-1<b[1]?[2,b[1]+e]:b:[d(b[0],a,e,c),d(b[1],a,e,c)]:b}(a[0],a[1],-1,0)[1]):a):a})

2要素配列を受け取ります。1はを表しλ、2はbruijnインデックス変数を表します。

テスト:

zero = [1,[1,[2,0]]]; // λλ0
succ = [1,[1,[1,[[2,1],[[[2,2],[2,1]],[2,0]]]]]]; // λλλ(1 ((2 1) 0))
console.log(JSON.stringify(reduce([succ,[succ,[succ,zero]]]))); // 0+1+1+1
// Output: [1,[1,[[2,1],[[2,1],[[2,1],[2,0]]]]]] = λλ(1(1(1 0))) = number 3

これは、問題の入出力仕様に準拠していません。
アンデルスカセオルグ

1

Python:1266文字(WCを使用して測定)

from collections import *;import re
A,B,y,c=namedtuple('A',['l','r']),namedtuple('B',['i','b']),type,list.pop
def ab(t):c(t,0);p=c(t,0);c(t,0);return B(p,tm(t))
def tm(t):return ab(t)if t[0]=='\\'else ap(t)
def at(t):
    if t[0]=='(':c(t,0);r=tm(t);c(t,0);return r
    if 96<ord(t[0][0])<123:return c(t,0)
    if t[0]=='\\':return ab(t)
def ap(t):
    l = at(t)
    while 1:
        r = at(t)
        if not r:return l
        l = A(l,r)
def P(s):return tm(re.findall(r'(\(|\)|\\|[a-z]\w*|\.)',s)+['='])
def V(e):o=y(e);return V(e.b)-{e.i} if o==B else V(e.l)|V(e.r)if o==A else{e}
def R(e,f,t):return B(e.i,R(e.b,f,t)) if y(e)==B else A(R(e.l,f,t),R(e.r,f,t))if y(e)==A else t if e==f else e
def N(i,e):return N(chr(97+(ord(i[0])-96)%26),e) if i in V(e)else i
def S(i,e,a): return A(S(i,e.l,a),S(i,e.r,a)) if y(e)==A else(e if e.i==i else B(N(e.i,a),S(i,R(e.b,e.i,N(e.i,a)),a)))if y(e)==B else a if e==i else e
def T(e):
    if y(e)==A:l,r=e;return S(l.i,l.b,r)if y(l)==B else A(T(l),r)if y(l)==A else A(l,T(r))
    if y(e)==B:return B(e.i,T(e.b))
    q
def F(e):o=y(e);return r'(\%s. %s)'%(e.i,F(e.b))if o==B else'(%s %s)'%(F(e.l),F(e.r)) if o==A else e
def E(a):
    try: return E(T(a))
    except NameError:print(F(a))
E(P(input()))

ロングショットでは最短ではありませんが、アルファリネームとOPポストにリストされているすべての例を正しく処理します。


これらの関数名の一部を短縮し、一部をラムダに変えることができます。また、あちこちに余分な空白がいくつかあります
Jo King

(1)4スペースのインデントを1つのスペースに置き換えると、かなりのバイトが節約されます。(2)except NameErrorジャストに置き換えることができますexceptか?(3)2文字の関数名は、1文字の名前に変更できます。(4)の周りにスペースがある割り当てがある場所がいくつかあります=。(5)if t[0]=='c'はに置き換えることができますif'c'==t[0]
エソランジングフルーツ

インデントやラムダなどのほとんどのフォーマット変更により1045バイト
Jo King

0

C ++(gcc)782 766 758 731バイト

#include <string>
#include <map>
#define A return
#define N new E
using S=std::string;using C=char;using I=int;S V(I i){A(i>8?V(i/9):"")+C(97+i%9);}S W(C*&s){C*b=s;while(*++s>96);A{b,s};}struct E{I t,i;E*l,*r;E(E&o,I d,I e){t=o.t;i=o.i+(o.i>=d)*e;t?l=N{*o.l,d,e},t-1?r=N{*o.r,d,e}:0:0;}E(I d,std::map<S,I>m,C*&s){t=*s-40?i=m[W(s)],0:*++s-92?l=N{d,m,s},r=N{d,m,++s},++s,2:(m[W(s+=2)]=d,l=N{d+1,m,s+=2},++s,1);}I R(I d){A t?t-1?l->t==1?l->l->s(d,0,*r),*this=*l->l,1:l->R(d)||r->R(d):l->R(d+1):0;}I s(I d,I e,E&v){t?t-1?l->s(d,e,v),r->s(d,e,v):l->s(d,e+1,v):i==d?*this={v,d,e},0:i-=i>d;}S u(I d){A t?t-1?S{"("}+l->u(d)+' '+r->u(d)+')':S{"(\\ "}+V(d)+". "+l->u(d+1)+')':V(i);}};S f(C*s){E a{0,{},s};for(I c=999;a.R(0)&&c--;);A a.u(0);}

オンラインでお試しください!

ここでの基本的な考え方は、コードがde Bruijnインデックスの考え方に基づいた内部表現を使用することです。ただし、参照変数のバインディングのラムダ深さを示すためにインデックスを逆にします。コード内:

  • E::tノードのタイプを表します-0は可変リーフノード、1はラムダノード、2は関数アプリケーションノードです。(ノードのアリティと一致するように選択されますが、これはたまたま可能です。)そしてE::land E::rは適切な子(ラムダノードのみE::l)でありE::i、可変リーフノードのラムダ深度インデックスです。
  • コンストラクターE::E(E&o,int d,int e)は、lambda-depth dの新しい場所に貼り付けるために、最初はlambda-depthにあった部分式を複製しd+eます。これにはd、少なくともでlambda-depthの変数をインクリメントするときよりも少ないlambda-depthの変数の保存が含まdeます。
  • E::sより大きい変数番号をデクリメントしながら、サブ式のv変数番号への置換を行います(そして、呼び出す必要がある場合のラムダ深さの増分を追跡する内部詳細です)。d*thisdeE::c
  • E::R実行する単一のベータ削減を検索し、ASTを介した先行予約検索に従って最上位または最左端のインスタンスを優先します。実行するリダクションが見つかった場合はゼロ以外を返し、見つからなかった場合はゼロを返します。
  • E::uto_string変数の合成名を使用して「人間が読める」文字列を再構成する型操作です。(Vヘルパー関数の少しのゴルフのため、athrough を含む名前のみを生成することに注意してくださいi。)
  • コンストラクターE::E(int d, std::map<std::string, int> m, char*&s)は、現在バインドされている変数名のラムダ深度インデックスへのsマッピングmに基づいて、入力文字列を解析して式ASTにします。
  • f 質問に答える主な機能です。

(TIOリンクでわかるように、コードは複数の文字を含む変数名を処理します。また、(\ a. (\ b. a))forの正解も取得します((\ f. (\ x. (f x))) (\ y. (\ x. y)))。また、解析コードが追加費用なしで変数シャドウイングを処理できるようになります。)


-16バイトは、一部はceilingcatのアイデアによるもので(これも独自に考案しました)、一部はに変更E*a=new E;E&a=*new E;てから変更したa->ためですa.

ceilingcatによる別のコメントによる-8バイトの増加(a.t三元からの割り当てを除外)

-27バイトのパーサーとクローンのコンストラクターへの変換 E


-1

C 637バイト

#define R return
#define E if(i>=M||j>=M)R-1;
#define H d[j++]
enum{O=40,C,M=3999};signed char Q[M],D[M],t[M],Z,*o=Q,*d=D,*T;int m,n,s,c,w;K(i,j,k){!Z&&(Z=t[O]=1)+(t[C]=-1);E;if(!o[i]){H=0;R 0;}if((c=t[o[i]]+t[o[i+1]])!=2||o[i+2]!=92){H=o[i++];R K(i,j,i);}for(i+=2,w=0;i<M&&o[i]&&c;++i)c+=t[o[i]],!w&&c==1?w=i:0;E;if(c){for(;H=o[i++];)E;R 0;}for(c=k+7,n=j;c<w&&j<M;++c)if(o[c]==o[k+4]){if(o[c+1]==46){d[n++]=o[k++];R K(k,n,k);}for(m=w+2;m<i-1&&j<M;)H=o[m++];}else H=o[c];E;Z=2;R K(i,j,i);}char*L(char*a){for(s=n=0;n<M&&(o[n]=a[n]);++n);if(n==M)R 0;for(;++s<M;){Z=0;if((n=K(0,0,0))!=-1&&Z==2)T=d,d=o,o=T;else break;}R n==-1||s>=M?0:d;}

このバージョンは補助変数を使用しません(したがって、これはラムダ計算が言うことを100%従わない...ここで他の多くのように...)。各変数の長さは1文字である必要があります(他の変数と同様)。テストコード:

#define P printf

main()
{char  *r[]={ "((\\ x. x) z)", 
              "((\\ x. x) (\\ y. (\\ z. z)))", 
              "(\\ x. ((\\ y. y) x))", 
              "((\\ x. (\\ y. x)) (\\ a. a))", 
              "(((\\ x. (\\ y. x)) (\\ a. a)) (\\ b. b))",
              "((\\ x. (\\ y. y)) (\\ a. a))",
              "(((\\ x. (\\ y. y)) (\\ a. a)) (\\ b. b))",
              "((\\ x. (x x)) (\\ x. (x x)))",
              "(((\\ x. (\\ y. x)) (\\ a. a)) ((\\ x. (x x)) (\\ x. (x x))))",
              "((\\ a. (\\ b. (a (a (a b))))) (\\ c. (\\ d. (c (c d)))))",
              "((\\ f. (\\ x. (f x))) (\\ y. (\\ x. y)))",
             0}, *y;
 int    w;

 for(w=0; r[w] ;++w)
   {y=L(r[w]);
    P("o=%s d=%s\n", r[w], y==0?"Error ":y);
   }
 R  0;
}

結果:

/*
637
o=((\ x. x) z) d=z
o=((\ x. x) (\ y. (\ z. z))) d=(\ y. (\ z. z))
o=(\ x. ((\ y. y) x)) d=(\ x. x)
o=((\ x. (\ y. x)) (\ a. a)) d=(\ y. (\ a. a))
o=(((\ x. (\ y. x)) (\ a. a)) (\ b. b)) d=(\ a. a)
o=((\ x. (\ y. y)) (\ a. a)) d=(\ y. y)
o=(((\ x. (\ y. y)) (\ a. a)) (\ b. b)) d=(\ b. b)
o=((\ x. (x x)) (\ x. (x x))) d=Error
o=(((\ x. (\ y. x)) (\ a. a)) ((\ x. (x x)) (\ x. (x x)))) d=(\ a. a)
o=((\ a. (\ b. (a (a (a b))))) (\ c. (\ d. (c (c d))))) d=(\ b. (\ d. (b (b (b (b (b (b (b (b d))))))))))
o=((\ f. (\ x. (f x))) (\ y. (\ x. y))) d=(\ x. (\ x. x))
*/

これはセミウンゴルフのものです:

#define R return
#define E if(i>=M||j>=M)R-1;
#define H d[j++]
enum{O=40,C,M=3999}; // assume ascii
signed char Q[M],D[M],t[M],Z,*o=Q,*d=D,*T;
int m,n,s,c,w;

K(i,j,k)
{!Z&&(Z=t[O]=1)+(t[C]=-1); //inizializza tabelle

 E;if(!o[i]){H=0;R 0;}
 if((c=t[o[i]]+t[o[i+1]])!=2||o[i+2]!=92)
      {H=o[i++]; R K(i,j,i);}
 for(i+=2,w=0;i<M&&o[i]&&c;++i)
         c+=t[o[i]],!w&&c==1?w=i:0;
 E;
 if(c){for(;H=o[i++];)E;R 0;} 
//  01234567w12 i
//  ((/ x. x) z)
//   x                 w              z
// o[k+4]..o[k+5];  o[k+7]..o[w];  o[w+2]..o[i-1]

// sostituzione
// sostituisce a x z in w e lo scrive in d
for(c=k+7,n=j;c<w&&j<M;++c)
      if(o[c]==o[k+4])
         {if(o[c+1]==46) // non puo' sostituire una variabile dove c'e' lambda
             {d[n++]=o[k++]; R K(k,n,k);}
          for(m=w+2;m<i-1&&j<M;++m)
                H=o[m];
         }
      else H=o[c];
 E;
 Z=2;
 R K(i,j,i);
}

char*L(char*a)
{for(s=n=0;n<M&&(o[n]=a[n]);++n);
 if(n==M)R 0;
 for(;++s<M;)
   {Z=0;
    n=K(0,0,0);
//    if(Z==2)printf("n=%d>%s\n", n, d);
    if(Z==2&&n!=-1)T=d,d=o,o=T;
    else break;
   }
 R n==-1||s>=M?0:d; 
}

指定には、/ではなく、\またはλが必要です。また、複数文字の変数名のサポートも必要です。さらに(あなたはこれを知っていますが、はい、まだ間違っています)、これは((/ f。(/ x。(fx)))(/ y。(/ x。y)))から( / x。(/ x。x))。
アンデルスカセオルグ

/を\に変更すると、複数文字の変数を使用できないという問題があります。他のテストを行う場合、これは他のソリューションにも
適用されます-RosLuP
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.