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-1f(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якщо це було функцією.
Зауважте, що всі команди, за винятком !просто натискання значення на стек, немає можливості виконати введення, і єдиний спосіб вивести що-небудь - це використовувати @. Програма інтерпретується шляхом оцінки команд по черзі, друку 0s або 1s кожного разу, коли викликається "сказати", та виходу з нього. Будь-яка поведінка, яка не описана тут (застосування бланка, нанесення стека довжиною 0 або 1, виклик "ланцюга" на бланку тощо), не визначено: перекладач може вийти з ладу, мовчки вийти з ладу, попросити ввести інформацію чи інше.
Завдання
Ваше завдання - написати перекладача для Shift. Потрібно взяти з STDIN, командного рядка або аргументу функції програму Shift, яку слід інтерпретувати, та роздрукувати в STDOUT або повернути отриманий (можливо нескінченний) вихід 0s і 1s. Якщо ви пишете функцію, ви повинні мати можливість отримати доступ до виходів нескінченної довжини якимось чином (генератор на 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..., де кількість 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часи за допомогою !, і результат цього додається до основного стеку.