EDIT: Як деякі з вас підозрювали, в офіційному перекладачі виникла помилка: порядок складання композиції .
був змінений. У мене було дві версії перекладача, і тут я використав неправильну. Приклади також були написані для цієї неправильної версії. Я зафіксував інтерпретатора у сховищі та наведені нижче приклади. Опис >
також було дещо неоднозначним, тому я це виправив. Крім того, вибачення за це зайняло так довго, я потрапив у деякі речі з реального життя.
EDIT2: У мого перекладача була помилка, реалізація .
якої відображалася в прикладах (вони покладалися на невизначену поведінку). Питання виправлено.
Вступ
Shift - це езотерична функціональна мова програмування, яку я створив пару років тому, але опублікував сьогодні. Він заснований на стеці, але також має автоматичну каррінг, як Haskell.
Специфікація
У Shift є два типи даних:
- Функції, які мають довільну позитивну сутність (кількість входів) і які повертають список результатів. Наприклад, функція, що дублює єдиний вхід, має arity 1, а функція, яка переміняє два входи, має arity 2.
- Пробіли, які всі однакові і не мають іншого призначення, крім того, щоб не бути функціями.
Програма Shift складається з нуля або більше команд , кожна з яких є одним символом ASCII. Всього 8 команд:
!
( застосувати ) з'являється функціяf
та значенняx
зі стека та застосовуєтьсяf
доx
. Якщоf
має arity 1, списокf(x)
додається на передню частину стека. Якщо у нього є арітіяn > 1
, до стеку висувається нова(n-1)
функція -aryg
. Він займає введення та повернення .x1,x2,...,xn-1
f(x,x1,x2,...,xn-1)
?
( blank ) проштовхує порожню до стека.+
( клон ) висуває в стек одинарну функцію, що дублює його вхід: будь-яке значенняx
відображається на[x,x]
.>
( shift ) висуває в стек одинарну функцію, яка приймає функціюn
-aryf
, і повертає функцію(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
є викликом . Якщо стек містить[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
якщо це було функцією.
Зауважте, що всі команди, за винятком !
просто натискання значення на стек, немає можливості виконати введення, і єдиний спосіб вивести що-небудь - це використовувати @
. Програма інтерпретується шляхом оцінки команд по черзі, друку 0
s або 1
s кожного разу, коли викликається "сказати", та виходу з нього. Будь-яка поведінка, яка не описана тут (застосування бланка, нанесення стека довжиною 0 або 1, виклик "ланцюга" на бланку тощо), не визначено: перекладач може вийти з ладу, мовчки вийти з ладу, попросити ввести інформацію чи інше.
Завдання
Ваше завдання - написати перекладача для Shift. Потрібно взяти з STDIN, командного рядка або аргументу функції програму Shift, яку слід інтерпретувати, та роздрукувати в STDOUT або повернути отриманий (можливо нескінченний) вихід 0
s і 1
s. Якщо ви пишете функцію, ви повинні мати можливість отримати доступ до виходів нескінченної довжини якимось чином (генератор на Python, лінивий список у Haskell тощо). Крім того, ви можете взяти інший вхід, число n
та повернути принаймні n
символи виводу, якщо він довший за n
.
Виграє найменший байт, а стандартні лазівки заборонені.
Випробування
Ця програма Shift друкує 01
:
?@!@@!
Починаючи зліва: натисніть на бляху, натисніть сказати , а потім застосуйте приказку до порожнього. Це виводить 0
. Потім натисніть " Скажіть двічі" і застосуйте друге слово до першого. Це виводить 1
.
Ця програма циклічно вічно, не даючи результатів:
$+.!!+!!
Натисніть на виклик і клонуйте , а потім застосуйте до них ланцюг (нам потрібно два !
s, оскільки ланцюг є бінарною функцією). Тепер стек містить функцію, яка бере один аргумент, дублює його і викликає першу копію на другу. З +!!
, ми дублюємо цю функцію і викликаємо її на себе.
Ця програма друкує 0010
:
?@$.++>!.!!.!!.!!!!+?/!!!@!@>!!!
Натисніть на бланк і скажіть . Потім складіть двійкову функцію, яка копіює її другий аргумент b
, потім копіює перший a
і складає його з собою, потім застосовує композицію до копії b
, повертаючись [a(a(b)),b]
. Застосуйте його до слова " слово" та "порожнє", а потім застосуйте " скажіть" до двох елементів, що залишилися в стеку.
Ця програма друкує 0
. Для кожного, !!!
що ви додаєте до нього, він друкує додатковий 0
.
?@+$>!>!+>!///!!>!>!.!!.!!.!!+!!!!
Натисніть на бланк і скажіть . Потім складіть потрійну функцію, яка приймає f,g,x
в якості вхідних даних і повертається [f,f,g,g(x)]
. Клоніруйте цю функцію та застосуйте її до себе, скажімо , та до порожнього. Ця програма не змінює стек, тому ми можемо застосувати цю функцію ще раз стільки разів, скільки нам захочеться.
Ця програма друкує нескінченну послідовність 001011011101111...
, де кількість 1
s завжди збільшується на одиницю:
@?/!@>!??/!!>!+.!!.!!.!!.+>!.!!$$$$+$>!>!$>!>!+>!$>!>!>!+>!>!///!!>!>!>!.!!.!!.!!.!!.!!.!!.!!.!!.!!.!!+!!!!!
У сховищі міститься примітка з приміткою.
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
часи за допомогою !
, і результат цього додається до основного стеку.