Prindeal (вимовляється prin -dee-al ) - нова езотерична мова програмування, яка містить лише чотири команди: pr int , in crement , de crement та al ias . Незважаючи на мінімалізм, складні математичні операції можна виконати в Prindeal, вміло поєднуючи чотири команди.
Ваше завдання в цій проблемі з гольфовим кодом - написати найкоротшу програму, яка може запустити Prindeal код.
Специфікація довга, але я намагався зробити це максимально зрозумілим, і я вважаю, що якщо ви докладете зусиль, щоб навчитися Prindeal, ви виявите, що це дуже елегантно!
Інтерпретація Prindeal
Попередня обробка
Перш ніж програму Prindeal можна буде інтерпретувати, ці речі потрібно видалити з неї в такому порядку:
- Все після
#
знака до кінця рядка він увімкнено, плюс#
сам. (Це коментарі.) - Пробіл пробілів на будь-якій лінії.
- Повністю порожні рядки.
Наприклад, програма Prindeal
p cat #The next line has 7 trailing spaces.
p dog
#p mouse
буде попередньо оброблено в
p cat
p dog
З цього моменту ми припустимо, що цей крок попередньої обробки був зроблений.
Змінні
Нам швидко потрібно визначити змінні, перш ніж показати, як вони використовуються.
Змінні (і посилання на змінні) - це те, що передається в аргументи команд Prindeal. Змінні завжди глобальні , тому зміни змінної, незалежно від того, де вони відбуваються, відображаються всюди.
Кожна змінна містить невід'ємне ціле число довільної точності (0, 1, 2, 3, ...). Змінні не потрібно попередньо ініціалізувати - вони завжди починаються зі значення 0 при першому використанні або виклику.
Ім’ям змінної може бути будь-який не порожній рядок буквено-цифрових знаків та підкреслення, який не починається з цифри - [a-zA-Z_][0-9a-zA-Z_]*
в регулярному вираженні . Вони чутливі до регістру, так spiny_lumpsuck3r
і Spiny_lumpsuck3r
різні змінні.
Виконання
Prindeal - імперативна мова програмування. Коли програма Prindeal запускається, її оператори виконуються зверху вниз, після чого програма закінчується.
Кожен не відступний рядок програми Prindeal - це оператор, який передбачає виконання єдиної команди, яка може або не може приймати аргументи.
Відступні рядки виникають лише після команд псевдоніму . Зокрема, рівно три рядки з відступом один пробіл виникають після кожної команди псевдоніму і вважаються його частиною. Тож твердження про псевдоніми дійсно мають чотири рядки. (Вони можуть бути одним рядком, чотири - просто читабельнішими.)
Виписки про не псевдоніми
За винятком псевдоніму , кожне твердження програми Prindeal має вигляд:
[command name] [argument 1] [argument 2] [argument 3] ...
Може бути довільна кількість аргументів (включаючи їх взагалі немає). Кожен аргумент - це завжди змінна або (як ми побачимо при обговоренні псевдоніму ) посилання на змінну .
Після завершення виконання кожне твердження позначається як відмова або успіх залежно від того, були помилки чи ні. (Це дійсно має значення лише тоді, коли ми обходимося з використанням псевдоніма .)
Вбудований друк , приріст та декремент - це твердження із наведеною формою. Ось що вони роблять:
print має ім'я команди
p
і бере один аргумент. Він друкує назву змінної, що передається, і її значення (у десятковій частині), розділене "=", а потім новий рядок. Це завжди позначено як успіх .Наприклад, програма Prindeal
p _MyVariable_321 p screaming_hairy_armadillo
виведе
_MyVariable_321 = 0 screaming_hairy_armadillo = 0
тому що всі змінні починаються з 0. (Потрібні пробіли до та після знака рівності.)
Приріст має ім'я команди
i
і бере один аргумент. Він збільшує значення змінної, переданої на 1. Це завжди позначено як успіх .Наприклад, програма
i alpaca p alpaca i alpaca p alpaca
виведе
alpaca = 1 alpaca = 2
Зверніть увагу на те, як
alpaca
збільшувались від 0 до 1, хоча до цього ніколи не зверталися.декремент має ім'я команди
d
і бере один аргумент. Якщо змінна, передана в ненульовій формі, її значення зменшується на 1, а заява позначена як успіх . Якщо передана змінна дорівнює 0, нічого не робиться, а заява позначається як збій .Наприклад, програма
i malamute p malamute d malamute #success p malamute d malamute #failure p malamute d akita #failure p akita
виведе
malamute = 1 malamute = 0 malamute = 0 akita = 0
Зауважте, що декрементування змінної зі значенням 0 є єдиним способом створення помилки .
Ім'я користувача Постановка і команди Пов'язаних
Команда alias має спеціальний синтаксис і є найпотужнішою, оскільки її можна використовувати для визначення нових команд. Ім'я команди псевдонім є, a
і оператор псевдоніма має форму:
a [name of new command]
[statement A]
[statement B]
[statement C]
Де кожен [statement X]
представляє будь-який не- псевдонім , тобто щось із формою [command name] [argument 1] [argument 2] [argument 3] ...
.
Ім'я псевдоніму [name of new command]
може бути будь-який не порожній рядок буквено-цифрових знаків і підкреслень, який не починається з цифри - [a-zA-Z_][0-9a-zA-Z_]*
в регулярному виразі.
(Це той самий набір імен, що і змінні, але псевдонім команди та змінні - це різні речі, які використовуються в різних місцях . Змінна може бути названа так само, як і команда без поганих наслідків.)
Коли виконується оператор псевдоніму , поряд із початковими чотирма p
i
d
a
командами додається нова команда . Нова команда може бути використана як [command name]
у висловлюваннях та викликана аргументами, як і будь-яка інша команда, яка псевдонімом .
Коли виконується оператор із псевдонімом імені команди, виконується рівно ще два оператори з його початкового оператора псевдоніма :
[statement A]
завжди працює[statement B]
запускається, якщо[statement A]
був a успіх[statement C]
запускається, якщо[statement A]
стався збій
Виписки A, B і C завжди ледачі , тобто вони оцінюються на льоту під час їх запуску.
Завершивши виконання, інсайдована команда позначається тим самим прапором успіху чи відмови, як і твердження B або C, залежно від того, хто з них був виконаний . ( псевдоніми тверджень самі не потрібно позначати, оскільки вони не можуть виникати всередині себе.)
Псевдонім Приклад 1
Скажіть, ми хочемо нової команди, яка збільшує змінну в
frog
два рази. Цей псевдонім твердження досягає цього:a increment_frog_twice i frog i frog d frog
Заява A (
i frog
) завжди виконується і завжди позначається як успіх, тому оператор B (i frog
) також завжди виконується, і зміннаfrog
таким чином збільшується на 2.increment_frog_twice
Команда завжди позначена як успіх, оскільки оператор B завжди виконується, а B - завжди a успіх . Заява C (d frog
) ніколи не виконується.Тож вихід на
a increment_frog_twice i frog i frog d frog p frog increment_frog_twice p frog
був би
frog = 0 frog = 2
Ми можемо узагальнити цей приклад, так що будь-яку змінну можна збільшити вдвічі, подавши команду aliase в аргумент.
В операторі псевдоніму додатні цілі числа 1, 2, 3 і т.д. представляють аргументи 1-го, 2-го, 3-го і т.д. (Ці аргументи можуть бути простими змінними або посиланнями на самі змінні.) Ці числа можуть відображатися лише в аргументах тверджень A, B і C у псевдонімі . Для них немає сенсу з’являтися в іншому місці.
Псевдонім Приклад 2
Це узагальнює останній приклад - будь-яка змінна, передана в,
increment_twice
буде нарощена на 2, оскільки1
це посилання на перший аргумент, переданий у:a increment_twice i 1 i 1 d 1 #never reached p toad increment_twice toad p toad
Вихід цієї програми буде
toad = 0 toad = 2
Тоді ми могли б отримати псевдонім ще одну команду, яка
increment_twice
містить два аргументи і викликає обидва:a increment_twice i 1 i 1 d 1 #never reached a increment_both_twice increment_twice 1 increment_twice 2 d 1 #never reached increment_both_twice platypus duck p platypus p duck
Вихід тут був би
platypus = 2 duck = 2
Важливо усвідомити, що в'язані команди можуть бути рекурсивними, оскільки саме тут лежить їх справжня сила. Наприклад, ми можемо скласти команду, яка встановлює будь-яку змінну, передану на 0:
Псевдонім Приклад 3
set_to_zero
Команда приймає один аргумент і встановлює його змінну в 0 і позначений як успіх , коли зроблено:a set_to_zero d 1 set_to_zero 1 i _dummy_ i oryx i oryx i oryx p oryx set_to_zero oryx p oryx
Вихід цієї програми буде
oryx = 3 oryx = 0
Що відбувається, це те, що коли
set_to_zero oryx
запускається,d 1
успішно зменшуєтьсяoryx
від 3 до 2, потімset_to_zero 1
викликається, що є тим же, що іset_to_zero oryx
знову викликати . Таким чином, процес повторюється, покиd 1
не стане збою , зупиняючи рекурсію і збільшуючи_dummy_
змінну, щоб досягти успіху .
Виклик
Напишіть програму, яка може виконати Prindeal-код точно так, як описано вище. Візьміть код Prindeal через stdin, командний рядок або як текстовий файл. Роздрукуйте вихід програми Prindeal на stdout або найближчу альтернативу вашій мові.
Крім того, ви можете написати функцію, яка приймає код як рядок і друкує або повертає вихідний рядок.
Крім того, ви можете припустити, що:
- Вхідний код Prindeal буде містити лише нові рядки та ASCII для друку та (необов'язково), що він закінчується порожнім рядком.
- Код введення буде дійсним Prindeal - добре сформований і синтаксично правильний.
- Запуск коду не призведе до нескінченних циклів і недійсних посилань на команди, які не були визначені, або аргументи, які не були задані.
- Назви команд
p
,i
,d
, іa
ніколи не буде поєднаним над. (Ви можете не припускати, що змінні не матимуть цих імен.)
Крім того, не має значення, чи не є ваші змінні значення справді цілими числами довільної точності, оскільки будуть перевірені лише числа менше ніж 1000. Також добре, якщо у вас мова має обмеження рекурсії (наприклад, Python ), з якими можуть зіткнутися більш складні програми Prindeal, поки працює тестова програма нижче.
Тестова програма
Ось велика програма Prindeal, яка будує операції додавання, множення та експоненціації за допомогою використання фіктивних змінних (починаючи з _
домовленості) та багатьох допоміжних псевдонімів:
#Command Definitions:
a s #flag as a success
i _
d _
d _
a f #flag as a failure
d _
d _
d _
a z #1 = zero
d 1
z 1
s
a n #1 = one
z 1
i 1
s
a move #2 += 1, 1 = zero
moveH 1 2
move 1 2
s
a moveH #move helper
d 1
i 2
f
a dupe #2 += 1, 3 += 1, 1 = zero
dupeH1 1 2 3
dupe 1 2 3
s
a dupeH1 #dupe helper
d 1
dupeH2 2 3
f
a dupeH2 #dupe helper
i 1
i 2
s
a copy #2 = 1
z 2
copyH 1 2
s
a copyH #copy helper
dupe 1 2 _copy
move _copy 1
s
a addTo #1 += 2
copy 2 _add
#testing comments #
move _add 1#in weird places # just because #
s
#it's a g##d idea
###
a add #1 = 2 + 3
#its a good idea
z 1
addH 1 2 3
s
##
#
a addH #add helper
#this is a comment
addTo 1 2 #as is this
addTo 1 3
s
a mul #1 = 2 * 3
mulH1 1 2
mulH2 1 3
s
a mulH1 #mul helper
z 1
copy 2 _mul
s
a mulH2 #mul helper
mulH3 1 2
mulH2 1 2
s
a mulH3 #mul helper
d _mul
addTo 1 2
f
a mulBy #1 *= 2
mul _mulBy 1 2
copy _mulBy 1
s
a pow #1 = 2^3
powH1 1 3
powH2 1 2
s
a powH1 #pow helper
n 1
copy 2 _pow
s
a powH2 #pow helper
powH3 1 2
powH2 1 2
s
a powH3 #pow helper
d _pow
mulBy 1 2
f
#Running Tests:
p A
p B
p C
n A #A = 1
n B #B = 1
add C A B #C = A + B = 1 + 1 = 2
p ____
p A
p B
p C
add B A C #B = A + C = 1 + 2 = 3
p ____
p A
p B
p C
mul d B C #d = B * C = 3 * 2 = 6
p ____
p d
mulBy d B #d = d * B = 6 * 3 = 18
p ____
p d
d A #A = A - 1 = 1 - 1 = 0
mulBy d A #d = d * A = 18 * 0 = 0
p ____
p d
pow A C B #A = C ^ B = 2 ^ 3 = 8
p ____
p A
p B
p C
pow A B C #A = B ^ C = 3 ^ 2 = 9
p ____
p A
p B
p C
pow C A B #C = A ^ B = 9 ^ 3 = 729
p ____
p A
p B
p C
(Якщо ви граєте з цим кодом, майте на увазі, що багато команд вийдуть з ладу, якщо одна і та ж змінна буде задана кілька разів як аргумент. Це можна легко виправити, але отриманий код довший.)
Ваш перекладач Prindeal повинен мати можливість отримати точний вихід:
A = 0
B = 0
C = 0
____ = 0
A = 1
B = 1
C = 2
____ = 0
A = 1
B = 3
C = 2
____ = 0
d = 6
____ = 0
d = 18
____ = 0
d = 0
____ = 0
A = 8
B = 3
C = 2
____ = 0
A = 9
B = 3
C = 2
____ = 0
A = 9
B = 3
C = 729
Оцінка балів
Виграє найкоротший код у байтах. Tiereaker переходить до більш раннього подання.
Бонус на брауні: Напишіть класну програму в Prindeal. Я реалізував додавання та множення, чи можна зробити віднімання чи ділення?
p
, а потімp p
, який би надрукував 1, правда?