РЕДАКТИРОВАТЬ: Как некоторые из вас подозревали, была ошибка в официальном переводчике: порядок композиции в .был обратный. У меня было две версии переводчика, и я использовал неправильную. Примеры были также написаны для этой неправильной версии. Я исправил переводчик в репозитории, и примеры ниже. Описание >также было немного двусмысленным, поэтому я исправил это. Кроме того, извинения за то, что это заняло так много времени, я застрял в некоторых реальных вещах.
РЕДАКТИРОВАТЬ 2: У моего интерпретатора была ошибка в реализации, .которая была отражена в примерах (они полагались на неопределенное поведение). Теперь проблема исправлена.
Введение
Shift - это эзотерический функциональный язык программирования, который я создал пару лет назад, но опубликовал сегодня. Он основан на стеке, но также имеет автоматическое каррирование, как Haskell.
Спецификация
В Shift есть два типа данных:
- Функции, которые имеют произвольную положительную арность (количество входов) и возвращают список выходов. Например, функция, которая дублирует свой единственный вход, имеет арность 1, а функция, которая меняет два своих входа, имеет арность 2.
- Бланки, которые все идентичны и не имеют никакой другой цели, кроме как не быть функциями.
Программа Shift состоит из нуля или более команд , каждая из которых представляет собой один символ ASCII. Всего 8 команд:
!( применить ) извлекает функциюfи значениеxиз стека и применяетсяfкx. Еслиfимеет арность 1, списокf(x)добавляется в начало стека. Если он имеет арностьn > 1, новая(n-1)-арная функция помещаетсяgв стек. Он принимает входные данные и возвращает .x1,x2,...,xn-1f(x,x1,x2,...,xn-1)?( пусто ) толкает пробел в стек.+( клон ) помещает в стек унарную функцию, которая дублирует входные данные: любое значениеxотображается на[x,x].>( shift ) помещает в стек унарную функцию, которая принимаетn-ary-функциюf, и возвращает(n+1)-ary-функцию,gкоторая игнорирует свой первый аргументx, вызываетfоставшиеся и привязываетxперед результатом. Например,shift(clone)это двоичная функция, которая принимает входные данныеa,bи возвращает[a,b,b]./( fork ) помещает в стек троичную функцию, которая принимает три входаa,b,cи возвращает,[b]еслиaпусто, и в[c]противном случае.$( Вызов ) толкает в стек двоичную функцию , которая появляется функциейfи значениеx, и применяетfкxточности так , как!делает..( цепочка ) помещает в стек двоичную функцию, которая выдает две функцииfиgи возвращает их композицию: функция,hкоторая имеет ту же самую арностьf, что и которая обычно принимает свои входные данные, применяетсяfк ним, а затем полностью применяетсяgк результату (вызывает это столько раз, сколько диктует его арность), с неиспользованными предметами из результата,fоставшимися в результатеh. Например, предположим, чтоfэто двоичная функция, которая клонирует свой второй аргумент иgназывается call . Если стек содержит[f,g,a,b,c]и мы.!!, то он содержит[chain(f,g),a,b,c]; если мы делаем!!дальше, тоfсначала применяетсяa,b, производя[a,b,b], затемgприменяется к первым двум элементам этого, поскольку его арность равна 2, производя[a(b),b], и стек, наконец, будет[a(b),b,c].@( скажем ) выдвигает унарную функцию, которая просто возвращает свой ввод и печатает,0если она была пустой, и1если это была функция.
Обратите внимание, что все команды, кроме !простого переноса значения в стек, не позволяют выполнять ввод, и единственный способ вывести что-либо - использовать @. Программа интерпретируется путем оценки команд одна за другой, вывода 0s или 1s при каждом вызове «say» и выхода. Любое поведение, не описанное здесь (применение пробела, применение стека длиной 0 или 1, вызов «цепочки» для пробела и т. Д.) Не определено: интерпретатор может аварийно завершить работу, потерять молчание, запросить ввод или что-то еще.
Задание
Ваша задача - написать переводчика для Shift. Он должен принимать из STDIN, командной строки или аргумента функции интерпретируемую программу Shift и печатать в STDOUT или возвращать результирующий (возможно, бесконечный) вывод 0s и 1s. Если вы пишете функцию, вы должны каким-то образом получить доступ к выходам бесконечной длины (генератор в Python, ленивый список в Haskell и т. Д.). В качестве альтернативы вы можете взять другой ввод, число nи вернуть хотя бы nсимволы вывода, если он длиннее n.
Побеждает меньшее количество байтов, и стандартные лазейки запрещены.
Тестовые случаи
Эта программа Shift печатает 01:
?@!@@!
Начиная слева: нажмите пробел, нажмите сказать , затем примените слово к пробелу. Это выводы 0. Затем дважды нажмите « сказать» и примените второе слово к первому. Это выводы 1.
Эта программа зацикливается навсегда, не выводя ничего:
$+.!!+!!
Нажмите call и clone , затем примените к ним цепочку (нам нужно два !s, так как цепочка является двоичной функцией). Теперь в стеке есть функция, которая принимает один аргумент, дублирует его и вызывает первую копию второго. С +!!, мы дублируем эту функцию и вызываем ее на себя.
Эта программа печатает 0010:
?@$.++>!.!!.!!.!!!!+?/!!!@!@>!!!
Нажмите пробел и скажите . Затем создайте двоичную функцию, которая копирует свой второй аргумент b, затем копирует первый aи составляет его с собой, затем применяет композицию к копии b, возвращая [a(a(b)),b]. Примените его, чтобы сказать и пусто, затем примените слово к двум элементам, остающимся в стеке.
Эта программа печатает 0. Для каждого, !!!что вы добавляете к нему, печатается дополнительный 0.
?@+$>!>!+>!///!!>!>!.!!.!!.!!+!!!!
Нажмите пробел и скажите . Затем составьте троичную функцию, которая принимает в f,g,xкачестве входных данных и возвращает [f,f,g,g(x)]. Клонируйте эту функцию и примените ее к себе, скажем , и к пробелу. Это приложение не меняет стек, поэтому мы можем применить функцию снова столько раз, сколько захотим.
Эта программа печатает бесконечную последовательность 001011011101111..., где число 1s всегда увеличивается на единицу:
@?/!@>!??/!!>!+.!!.!!.!!.+>!.!!$$$$+$>!>!$>!>!+>!$>!>!>!+>!>!///!!>!>!>!.!!.!!.!!.!!.!!.!!.!!.!!.!!.!!+!!!!!
Репозиторий содержит аннотированную версию.
f(x1, x2, ..., xn)и g(y1, y2, ..., ym). Вызов .вызывает оба из них и толкает функцию h(z1, z2, ..., zn). Теперь вы можете съесть все эти аргументы, постепенно вычеркивая их !. После nтаких приложений оставшаяся функция имела только один аргумент, и в этот момент она вычисляет f(z1, z2, ..., zn)(то есть fприменяется ко всем аргументам, в которых вы каррировали), который выдвигает некоторые новые значения, а затем сразу же получает mзначения из стека и вызывает gих.
.работает точно так же, как описал Мартин, за исключением того, что если он fвозвращает список mзначений, меньших значений, результат не определен (композиция имеет арность n, поэтому она не может использовать больше аргументов из стека). По сути, выходные данные fиспользуются в качестве временного стека, на который gпроталкиваются и применяются mвремена !, а результат этого добавляется в основной стек.