Напишите интерпретатор для нетипизированного лямбда-исчисления

45

Задача состоит в том, чтобы написать интерпретатор для нетипизированного лямбда-исчисления, используя как можно меньше символов. Мы определяем нетипизированное лямбда-исчисление следующим образом:

Синтаксис

Существуют следующие три вида выражений:

  • Лямбда-выражение имеет форму, (λ x. e)где xможет быть любое допустимое имя переменной и eлюбое допустимое выражение. Здесь xназывается параметром и eназывается телом функции.

    Для простоты мы добавим еще одно ограничение: не должно быть переменной с тем же именем, что и в xнастоящее время в области видимости. Переменная начинает находиться в области видимости, когда ее имя появляется между и .и перестает быть в области видимости соответствующей области ).

  • Функция приложения имеет вид (f a)где fи aявляются юридическими выражениями. Здесь fназывается функция и aназывается аргументом.
  • Переменная имеет вид xгде xдопустимое имя переменной.

Семантика

Функция применяется путем замены каждого вхождения параметра в теле функции своим аргументом. Более формально выражение формы ((λ x. e) a), где xэто имя переменной и eи aявляются выражениями, оценивает (или уменьшает) к выражению e'где e'является результатом замены каждого вхождения xв eс a.

Нормальная форма - это выражение, которое не может быть оценено в дальнейшем.

Соревнование

Ваша миссия, если вы решите принять ее, - написать интерпретатор, который принимает в качестве входных данных выражение нетипизированного лямбда-исчисления, не содержащее свободных переменных, и выдает в качестве выходных данных нормальную форму выражения (или выражение, альфа-конгруэнтное ему) , Если выражение не имеет нормальной формы или не является допустимым выражением, поведение не определено.

Решение с наименьшим количеством символов выигрывает.

Пара заметок:

  • Входные данные могут быть либо прочитаны из стандартного ввода, либо из имени файла, заданного в качестве аргумента командной строки (вам нужно только реализовать одно или другое, а не оба). Вывод идет в стандартный вывод.
  • В качестве альтернативы вы можете определить функцию, которая принимает входные данные в виде строки и возвращает выходные данные в виде строки.
  • Если символы не ASCII для вас проблематичны, вы можете использовать \символ обратной косой черты ( ) вместо λ.
  • Мы считаем количество символов, а не байтов, поэтому даже если ваш исходный файл в кодировке Unicode λ считается одним символом.
  • Допустимые имена переменных состоят из одной или нескольких строчных букв, то есть символов между a и z (нет необходимости поддерживать буквенно-цифровые имена, прописные буквы или нелатинские буквы - хотя это, конечно, не сделает ваше решение недействительным).
  • Что касается этой проблемы, то круглые скобки не являются обязательными. Каждое лямбда-выражение и каждое приложение функции будут заключены ровно в одну пару скобок. Имя переменной не будет заключено в круглые скобки.
  • Синтаксический сахар, такой как запись (λ x y. e)для (λ x. (λ y. e)), не нуждается в поддержке.
  • Если для оценки функции требуется глубина рекурсии более 100, поведение не определено. Это должно быть более чем достаточно, чтобы быть реализованным без оптимизации на всех языках, и все же достаточно большим, чтобы можно было выполнять большинство выражений.
  • Вы также можете предположить, что интервал будет таким же, как в примерах, то есть без пробелов в начале и конце ввода или перед λили или .и ровно через один пробел после a .и между функцией и ее аргументом и после a λ.

Пример ввода и вывода

  • Входные данные: ((λ 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 в церковных цифрах.

sepp2k
источник
1
Можем ли мы предположить, что к строке не будет добавлен или добавлен пробел, и что пробел в противном случае будет таким, как указано в примере ввода? То есть без пробелов между скобками, между точкой и именем параметра и другими экземплярами пробелов ровно 1 пробел.
JPvdMerwe
@JPvdMerwe: Да, хорошая мысль, вы можете это предположить.
sepp2k
Есть ли свободные переменные? Я имею в виду переменные, не связанные лямбда, как в выражении (\y. a).
FUZxxl
3
Многие или все решения здесь не в состоянии осуществить замену, избегающую захвата! Вы должны добавить тестовый пример типа ((λf. (Λx. (Fx))) (λy. (Λx. Y))), который должен оценивать как (λx. (Λz. X)), нет (λ x. (λ x. x)).
Андерс Касеорг
1
@ sepp2k Рассматривали ли вы добавление ((λ f. (λ x. (fx))) (λ y. (λ x. y))) в качестве контрольного примера и неприятие текущего ответа, который выдает некорректно (λ x. (λ х. х))?
Андерс Касеорг

Ответы:

36

Новые:

Я сократил его до 644 символов , я разложил части cEll на cOpy и Par; кэшировал вызовы cell и cdr во временные локальные переменные и перемещал эти локальные переменные в глобальные переменные в «терминальных» (то есть нерекурсивных) функциях. Кроме того, десятичные константы короче литералов символов и это неприятное дело ...

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

... правильно определяет строчные буквы (при условии ASCII), но также принимает любую из `{|} ~. (То же самое наблюдение об ASCII сделано в этом превосходном видео о UTF-8 .)

Эт альт: |

#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;}

Ранее:

Могу ли я получить несколько голосов за усилия? Я работаю в этот день и ночь в течение недели. Я выкопал оригинальную статью Маккарти, и меня мучила ошибка в самой газете, пока я не прочитал приложение к Полу Грэму « Корни Лиспа» . Я так отвлекся, что заперся в своем доме, а затем совершенно забыл, пока не вернулся домой в ту ночь в 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;
}

Вот блок перед окончательными сокращениями. Трюки здесь - это целочисленные курсоры вместо указателей (использующие поведение «implicit int») и использование «нуля памяти»: это char*nуказатель «new» или «next» в свободное пространство. Но иногда я записываю строку в память, затем вызываю strlen и увеличиваю n; эффективно используя память, а затем выделяя ее, после того как размер будет легче вычислить. Вы можете видеть, что это в значительной степени прямо из статьи Маккарти, за исключением того, cell()какие интерфейсы между функциями и строковым представлением данных.

#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
Я нашел еще несколько трюков, чтобы спасти персонажа или двух, но ничего радикального. sprintf(n,...);n+=strlen(n)+1;Это лучше, поскольку n+=sprintf(n,...)+1;инвертирование синтаксиса массива x[m]вместо того, m[x]чтобы позволить мне заменить все косвенные переменные макросом «postfix» #define M [m]..., x Mкоторый сохраняет 1 символ и дает «свободный» разрыв строки, поскольку для разделения токенов необходим пробел.
Люзер Дрог
Похоже, есть некоторые сходства с этим и jar.2 xlisp 4.0 из IOCCC 1989 года .
Люзер Дрог
Я попытался расширить это до более полного интерпретатора Lisp .
Люсер Дрог
Прокомментированный код // um ...перебирает строку и считает скобки, пока не найдет подходящую близкую точку на правильном уровне вложенности.
luser droog
1
Это неправильно оценивает ((\ f. (\ X. (Fx)))) (\ y. (\ X. Y))) в (\ x. (Fx)).
Андерс Касеорг
22

Двоичное лямбда-исчисление 186

Программа показанная в шестнадцатеричном дампе ниже

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 в церковных цифрах

Сохраните вышеуказанный шестнадцатеричный дамп с помощью xxd -r> symbolic.Blc

Возьмите переводчика blc с http://tromp.github.io/cl/uni.c

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
Где вы получаете фактор х3? Вы на самом деле поднимаете вопрос о том, что языки с меньшими исходными алфавитами, такими как BF, наказываются. Для правильного сравнения все размеры должны быть выражены в битах, а символы BF занимают только 3 бита каждый. Большинству других языков нужно 7 бит для ASCII, некоторые используют все 8.
Джон Тромп
1
Кстати +1 Это чертовски круто!
Люзер Дрог
1
Если фрактран в фрактране приемлем, я не понимаю, почему это вообще должно быть проблемой. Вы не можете прочитать это? Вы хотите? Учиться!
Люзер Дрог
1
Что нужно сделать, чтобы он прочитал фактический формат ввода? Я думаю, что именно здесь вы теряете потенциальные противники.
luser droog
14

Haskell, 342 323 317 305 символов

На момент написания статьи это единственное решение, которое оценивает ((λ f. (Λ x. (Fx))) (λ y. (Λ x. Y))) правильный результат (λ x. (Λ z. х)) а не (Х х. (Х х. х)). Корректная реализация лямбда-исчисления требует замены , избегающей захвата , даже при упрощенной гарантии этой задачи, что никакая переменная не затеняет другую переменную в своей области видимости. (Моя программа работает даже без этой гарантии.)

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"++))

Заметки:

  • Это выполняется в GHC 7.0, как и требовалось, поскольку этот вызов был задан в январе 2011 года. Если бы мне было позволено принять GHC 7.10, это было бы на 13 символов короче .

Неуправляемая версия с документацией.

Андерс Касеорг
источник
ваша прога в ideone haskell compiler для ввода ((\ x. x) (\ y. (\ z. z))) возвращает "ошибку времени выполнения" даже в ((\\ x. x) (\\ y. ( \\ z. z))) ... что значит "лекс" в Haskell?
РосЛюП
2
@RosLuP Моя программа принимает λ, а не \.
Андерс Касеорг
введите это значение ((λ x. x) (λ y. (λ z. z))) в ideone.com return: время ошибки времени выполнения: 0 память: 4876 сигнал: -1
RosLuP
1
@RosLuP Ideone, кажется, сломал поддержку Unicode. Попробуйте использовать командную строку или другой онлайн-интерпретатор (например, он работает на Rextester ).
Андерс Касеорг,
2
@codeshot Автор вопроса уже прокомментировал, что ((λ f. (λ x. (fx))) (λ y. (λ x. y))) ↦ (λ x. (λ z. x)) является правильным для эта проблема (так же, как реальное лямбда-исчисление).
Андерс Касорг
13

Python - 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())
JPvdMerwe
источник
Это выглядит красиво, но, похоже, не работает. Я добавил несколько примеров входов и выходов, для которых ваш код дает неправильные результаты.
sepp2k
1
Это не в состоянии сделать замену избежания захвата. Например, ((\ f. (\ X. (Fx))) (\ y. (\ X. Y))) неверно оценивается как (\ x. (\ X. X)).
Андерс Касеорг
1
Почему это помечено как ответ, когда оно едва работает? Вы пробовали данные входы и выходы автора?
rbaleksandar
1
Предоставленные автором тестовые примеры недостаточны для демонстрации ошибок в этом ответе.
Андерс Касеорг
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))

Решение еще не полностью закончено, но уже почти нечитаемо.

Говард
источник
Привет зависть, мой старый друг :)
Luser droog
Это не в состоянии сделать замену избежания захвата. Например, ((\ f. (\ X. (Fx))) (\ y. (\ X. Y))) неверно оценивается как (\ x. (\ X. X)).
Андерс Касеорг
В дополнение к вышеупомянутой ошибке захвата, это также неправильно оценивает (\ y. (\ Xx. ((\ X. Xx) y))) в (\ y. (\ Xx. Yy)), где произведена чрезмерная замена строки несуществующая переменная yy.
Андерс Касеорг
3

Изменить: проверьте мой ответ ниже для 250 под чистым JavaScript.

2852 243 символа, использующих LiveScript (без регулярных выражений! Не полностью в гольф - можно улучшить)

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=9, как указано на ОП.

Если кому-то интересно, вот расширенная версия с некоторыми комментариями:

# 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]
MaiaVictor
источник
Это не соответствует входным и выходным характеристикам проблемы.
Андерс Касеорг,
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-только я знаю, что такое Арк, но часть «Уотерхаус» подсказала мне, что нужен какой-то определенный диалект. Вы заставили это бежать?
Андерс Касеорг
@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*/
RosLuP
источник
Спецификация требует \ или λ, а не /. Также требуется поддержка многобуквенных имен переменных.
Андерс Касеорг
Символ '\ n' и т. д. '\' используется по-другому, вместо него лучше использовать '/'
RosLuP
1
Тем не менее, задача состоит в том, чтобы удовлетворить спецификацию, а не сделать ее лучше.
Андерс Касеорг
я написал кое-что для того, чтобы быть немного более соответствующим ... но размер взорвался ...
RosLuP
1
932 байта
ceilingcat
1

Haskell 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

Получил 231 с JavaScript / без регулярных выражений

(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 обозначает переменную индекса Брейна.

Тест:

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
MaiaVictor
источник
Это не соответствует входным и выходным характеристикам проблемы.
Андерс Касеорг
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()))

Не самый короткий по дальности, но он правильно обрабатывает альфа-переименование и все примеры, перечисленные в постах ОП.

Бьорн Линдквист
источник
Вы можете сократить некоторые из этих имен функций и превратить некоторые из них в лямбды. У вас также есть некоторые лишние пробелы здесь и там
Джо Кинг
(1) Замена отступа в 4 пробела одним пробелом сэкономит немало байтов. (2) Можете ли вы заменить except NameErrorпросто except? (3) Двухсимвольные имена функций могут быть переименованы в односимвольные имена. (4) Есть несколько мест, где у вас есть задания, в которых есть пробелы =. (5) if t[0]=='c'можно заменить на if'c'==t[0].
Esolanging Fruit
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);}

Попробуйте онлайн!

Основная идея здесь заключается в том, что в коде используется внутреннее представление, основанное на идее индексов де Брюйна, за исключением того, что я обращаюсь к индексам в обратном порядке, чтобы указать лямбда-глубину привязки указанной переменной. В коде:

  • E::tпредставляет тип узла - 0 для листового узла переменной, 1 для лямбда-узла и 2 для узла приложения функции. (Выбранный таким образом, чтобы он совпадал с арностью узла, что просто возможно). Тогда E::lи E::rявляются подходящими дочерними элементами (только E::lдля лямбда-узла), и E::iэто индекс лямбда-глубины для переменного конечного узла.
  • Конструктор E::E(E&o,int d,int e)клонирует подвыражение, которое изначально было на лямбда-глубине, dдля вставки в новое место на лямбда-глубине d+e. Это предполагает сохранение переменных на лямбда-глубине меньше, чем dпри увеличении переменных на лямбда-глубине хотя бы dна e.
  • E::sделает замену подвыражения vв переменное число dв *thisто время как декремент переменного числа больше deвнутренняя деталь отслеживания лямбды-приращения глубины для того, когда она должна вызова E::c).
  • E::Rвыполняет поиск одного бета-сокращения, предпочитая самые верхние или самые левые экземпляры в соответствии с предварительным поиском через AST. Он возвращает ненулевое значение, если он нашел сокращение для выполнения, или ноль, если он не нашел ни одного.
  • E::uявляется to_stringоперацией типа, которая восстанавливает «читаемую человеком» строку, используя синтетические имена для переменных. (Обратите внимание, что из-за небольшого влияния Vвспомогательной функции он будет генерировать только имена, содержащие aдо i.)
  • Конструктор E::E(int d, std::map<std::string, int> m, char*&s)выполняет синтаксический анализ входной строки sв выражении AST, основываясь на отображении mимен связанных в настоящее время переменных в индексы глубины лямбды.
  • f является основной функцией ответа на вопрос.

(Как вы можете видеть по ссылке TIO, код обрабатывает имена переменных с несколькими символами, и он также получает правильный ответ (\ a. (\ b. a))для ((\ f. (\ x. (f x))) (\ y. (\ x. y))). Также случается, что код синтаксического анализа может обрабатывать затенение переменных без дополнительных затрат.)


-16 байтов, частично из-за идеи потолочной кошки (которую я также придумал независимо), и частично из-за изменения E*a=new E;в E&a=*new E;и затем изменения a->вa.

-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; 
}
RosLuP
источник
Спецификация требует \ или λ, а не /. Также требуется поддержка многобуквенных имен переменных. Кроме того (я знаю, что вы знаете об этом, но да, это все еще неправильно), это неправильно оценивает ((/ f. (/ X. (Fx))) (/ y. (/ X. Y))) к ( / х. (/ х. х)).
Андерс Касеорг
Я изменяю / на \ есть проблема, не допускающая переменную из нескольких символов. если проверить что-то другое, то это тоже для другого решения
RosLuP