Prindeal (произносится как prin-dee-al ) - это новый эзотерический язык программирования, который имеет только четыре команды: pr int , in crement , de crement и al ias . Несмотря на минимализм, в 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 , increment и expment являются операторами с вышеуказанной формой. Вот что они делают:
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 является единственным способом вызвать ошибку .
Псевдоним Постановка и команды Связанных
Команда псевдонимов имеет специальный синтаксис и является наиболее мощной, поскольку ее можно использовать для определения новых команд. Псевдоним имя команды 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]операторы in и вызываться с аргументами, как и любая другая команда без псевдонимов .
Когда выполняется оператор с псевдонимом команды , запускаются ровно еще два оператора из его исходного оператора псевдонима :
[statement A]всегда работает[statement B]выполняется, если[statement 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 всегда является успех . Оператор C (d frog) никогда не выполняется.Итак, вывод
a increment_frog_twice i frog i frog d frog p frog increment_frog_twice p frogбыло бы
frog = 0 frog = 2
Мы можем обобщить этот пример, чтобы любая переменная могла быть увеличена в два раза, дав аргументированной команде-псевдониму.
В операторе псевдонима положительные целые числа 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 через стандартный ввод, командную строку или в виде текстового файла. Распечатайте вывод программы Prindeal на стандартный вывод или ближайшую альтернативу вашего языка.
Кроме того, вы можете написать функцию, которая принимает код в виде строки и печатает или возвращает строку вывода.
Кроме того, вы можете предположить, что:
- Входной код 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
счет
Самый короткий код в байтах побеждает. Tiebreaker переходит на более раннюю подачу.
Бонус Брауни: написать классную программу на Приндее. Я реализовал сложение и умножение, вы можете сделать вычитание или деление?
p, а затемp p, что бы вывести 1, верно?