Esolang——2.brainfuck编程(一)
在上一篇文章中,我们写出了Helloworld程序。但是显然,如果一个编程语言不应只能输出helloworld,它还要能干其他事情。接下来,我们将使用其进行编程。
在开始前注意两点:
第一,本教程所使用的解释器采用每个单元数据范围0~255且为超出范围后取模的规定。
例如,若指针当前所指位置值为0,则执行'-'命令后值变为255
第二,为了方便,我们约定如下程序中出现的变量名指的是将指针移到变量对应位置。
例如,设数组为a[],若指针在a[0],变量x在a[1],变量y在a[3],则x[y+x-]y即为>[>>+<<-]>>
第三,这里讲的是如何将平时的程序给翻译成bf程序,但brainfuck的一大魅力就是压缩程序长度。用这里的方法写出来的程序只是一种解,不一定是最优解。
好的,我们开始吧。

注释
假设变量x==0
x[
在这里你想打什么就打什么,只要保证'['和']'一比一出现即可,
如果出现右中括号先于左中括号出现的情况可以在最外层多加一层中括号
]
x=0
x[-]
我们只需要把变量循环减到0就好了
x=y,y=0
x[-]y[x+y-]x
相当于把y移到x的位置上,同时自己清零
x=y
与上面类似。为了恢复y的值,我们可以设置一个中间变量tmp,把y同时复制到x和tmp,再把tmp复制回y。
x[-]tmp[-]y[x+tmp+y-]tmp[y+tmp-]x
x+=y
tmp[-]y[x+tmp+y-]tmp[y+tmp-]x
只需要把x=y中的清空x变量删去即可。
那么x=y+z怎么办?我们可以分别运行x=y,x+=z。这样我们就获得了结果。
x-=y
tmp[-]y[x-tmp+y-]tmp[y+tmp-]x
类似x+=y不解释
while(x){code;}
x[
code;
x]
"code;"代指可以替换想要的代码。
if(x){code;}
if(x){code;}可以写成
while(x){
code;
tmp=x,x=0;
}
x=tmp,tmp=0;
翻译一下
tmp[-]x[
code;
x[tmp+x-]]tmp[x+tmp-]
或者,我们可以使用一种更简单的方法
tmp[-]x[
code;
tmp]
但这种方法的一大问题就是程序执行完后指针位置不同。如果x==0,那指针最后在x,否则指针最后在tmp。如果一定要这么写但又不希望指针移动可以采用下面的方式。
向某一方向移动直至当前指针所指元素为0
向左
[<]
向右
[>]
这样就可以把原本不同位置的指针定位到同一个特定的位置
或者我们也可以拓展一下
向某一方向移动直至当前指针所指元素为1
向左
-[+<-]+
向右
-[+>-]+
如果想要其他值,就把减号和加号的个数改动一下就好了,不过这里比较使用的是常量。
if(x){code0;}else{code1;}
该语句可以拆成
tmp=1;
if(x){
code0;
tmp=0;
}
if(tmp){
code1;
}
如果我们采用通解写if,那么翻译一下后获得
tmp0[-]+tmp1[-]
x[
code0;
tmp0-x[tmp1+x-]]tmp1[x+tmp1-]
tmp0[
code1;
tmp0-]
tmp0[-]+tmp1[-]x[
code0;
tmp0-x[tmp1+x-]]tmp1[x+tmp1-]tmp0[
code1;
tmp0-]
如果要采用其他形式的if,相应替换即可。比如下面
x=!x
实际上其可写成
if(x){
x=0;
}else{
++x;
}
由于我们不需要保留x,所以相较于上面的if…else…,这里可以压缩
tmp[-]+x[
tmp-x[-]]tmp[
x+
tmp-]x
tmp[-]+x[tmp-x[-]]tmp[x+tmp-]x
if(x==0){code;}
实际上其可以写成
if(x){
;
}else{
code;
}
翻译一下即得
tmp0[-]+tmp1[-]x[
tmp0-x[tmp1+x-]]tmp1[x+tmp1-]tmp0[
code;
tmp0-]
tmp0[-]+tmp1[-]x[tmp0-x[tmp1+x-]]tmp1[x+tmp1-]tmp0[
code;
tmp0-]
for(x=y;x;--x){code;}
可以写成
x=y;
while(x){
code;
--x;
}
翻译一下
x[-]tmp[-]y[x+tmp+y-]tmp[y+tmp-]x[
code;
x-]
x*=y
x*y可以写作y个x相加,我们可以使用上述for循环,即
for(tmp=x,x=0;tmp;--tmp){
x+=y;
}
翻译一下即得
tmp0[-]
tmp1[-]x[tmp1+x-]tmp1[
y[tmp0+x+y-]tmp0[y+tmp0-]
tmp1-]x
tmp0[-]tmp1[-]x[tmp1+x-]tmp1[y[tmp0+x+y-]tmp0[y+tmp0-]tmp1-]x
x**=y
x**y可以写作y个x相乘,
tmp0=x,x=0;
x++;
for(tmp1=y;tmp1;--tmp1){
x*=tmp0;
}
tmp0=0;
翻译一下
tmp0[-]tmp1[-]tmp2[-]
x[tmp0+x-]
+
y[tmp1+tmp2+y-]tmp2[y+tmp2-]tmp1[
tmp3[-]x[tmp2+x-]tmp2[tmp0[tmp3+x+tmp0-]tmp3[tmp0+tmp3-]tmp2-]
tmp1-]
tmp0[-]x
tmp0[-]tmp1[-]tmp2[-]x[tmp0+x-]+y[tmp1+tmp2+y-]tmp2[y+tmp2-]tmp1[tmp3[-]x[tmp2+x-]tmp2[tmp0[tmp3+x+tmp0-]tmp3[tmp0+tmp3-]tmp2-]tmp1-]tmp0[-]x
x/=y
tmp0=x,x=0;
tmp1=0;
while(tmp0){
for(tmp1=y;tmp1;--tmp1){
--tmp0;
if(tmp0==0){
--tmp1;
if(tmp1){ //如果tmp0==0时tmp1!=0,说明没有整除,x要减掉一个后面多加的1
--x;
tmp1=0;
}
++tmp1;
}
}
++x;
}
翻译后
tmp0[-]tmp1[-]tmp2[-]tmp3[-]
x[tmp0+x-]
tmp0[
y[tmp1+tmp2+y-]tmp2[y+tmp2-]tmp1[
tmp2+tmp0-[tmp2-tmp0[tmp3+tmp0-]]tmp3[tmp0+tmp3-]tmp2[
tmp1-[
x-
tmp1[-]]
+
tmp2[-]]
tmp1-]
x+
tmp0]x
tmp0[-]tmp1[-]tmp2[-]tmp3[-]x[tmp0+x-]tmp0[y[tmp1+tmp2+y-]tmp2[y+tmp2-]tmp1[tmp2+tmp0-[tmp2-tmp0[tmp3+tmp0-]]tmp3[tmp0+tmp3-]tmp2[tmp1-[x-tmp1[-]]+tmp2[-]]tmp1-]x+tmp0]x
x%=y
while(x){
tmp0=x;
for(tmp1=y;tmp1;--tmp1){
x--;
if(x==0){
tmp1--;
if(tmp1==0){
tmp0=0;
}
tmp1=1;
}
}
}
x=tmp0,tmp0=0;
tmp1[-]tmp2[-]tmp3[-]
x[
tmp0[-]x[tmp0+tmp1+x-]tmp1[x+tmp1-]
y[tmp1+tmp2+y-]tmp2[y+tmp2-]tmp1[
tmp2+x-[tmp2-x[tmp3+x-]]tmp3[x+tmp3-]tmp2[
tmp3+tmp1-[tmp3-tmp1[-]]tmp3[tmp0[-]tmp3-]
tmp1+
tmp2-]
tmp1-]
x]
tmp0[x+tmp0-]x
(这个是我自己写的,期待有大佬优化一下)
tmp1[-]tmp2[-]tmp3[-]x[tmp0[-]x[tmp0+tmp1+x-]tmp1[x+tmp1-]y[tmp1+tmp2+y-]tmp2[y+tmp2-]tmp1[tmp2+x-[tmp2-x[tmp3+x-]]tmp3[x+tmp3-]tmp2[tmp3+tmp1-[tmp3-tmp1[-]]tmp3[tmp0[-]tmp3-]tmp1+tmp2-]tmp1-]x]tmp0[x+tmp0-]x
练习
一、承接输入
猫程序(cat program(我真不知道该怎么翻))是一个将标准输入复制到标准输出的程序。
输入:一串不定长字符串,以换行符结尾
输出:输入的字符串(包括换行符)
示例:输入:Hello, World!\n
输出:Hello, World!\n
二、输入数字
brainfuck中所有的输入都是字符形式,所以没办法直接输入数字。
输入:一个范围在0~255间的正整数,以换行符结尾
输出:该数字对应ASCII字符
示例:输入:97\n
输出:a
三、数字输出
输入:一个ASCII字符
输出:该ASCII字符对应十进制正整数
示例:输入:a
输出:97
四、平方数计算
输入:一个范围在0~15之间的整数,以换行符结尾
输出:该数字平方数
示例:输入:12\n
输出:144

参考:
[1]Brainfuck algorithms - Esolang https://esolangs.org/wiki/Brainfuck_constants