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, верно?