Prindeal(明显的首席- DEE-AL)是一种新的深奥的编程语言,只有四个命令:PR INT,在 crement,德 crement,和人 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程序中的每条非缩进行都是一条语句,该语句涉及单个命令的执行,该命令可以带或不带参数。
缩进行仅在别名命令之后出现。具体来说,在每条别名命令之后,恰好有三行缩进一个空格, 并被视为其中一部分。因此,别名语句实际上是四行。(它们可能是一行,而四行则更具可读性。)
非别名语句
除alias之外,Prindeal程序中的每个语句都具有以下形式:
[command name] [argument 1] [argument 2] [argument 3] ...
可能有任意数量的参数(根本没有参数)。每个参数始终是变量,或者(如我们在讨论别名时所看到的)是对变量的引用。
一旦执行完,根据是否遇到错误,将每个语句标记为失败或成功。(这只有在我们开始使用别名时才真正重要。)
内置的print,increment和decrement是具有上述形式的语句。这是他们的工作:
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]
是一个失败
执行完毕后,将以与语句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
我们可以对此示例进行概括,以便通过为别名命令提供参数,可以将任何变量增加两次。
在别名语句中,正整数1、2、3等表示传递给别名命令的1st,2nd,3rd等参数。(这些参数可以是普通变量或对变量本身的引用。)这些数字只能出现在别名语句中的语句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代码的程序。通过stdin,命令行或文本文件获取Prindeal代码。将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
计分
以字节为单位的最短代码获胜。Tiebreaker进入较早提交。
布朗尼奖金:在Prindeal中编写一个很棒的程序。我实现了加法和乘法,可以减法或除法吗?
p
然后再递增,p p
哪个会打印1,对吗?