这篇文章上次修改于 420 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

Brainfuck 语言是什么?

Brainfuck 的代码大概长这样:

++++++++++[>+++++++>++++++++++>+++>+++++++++>+<<<<<-]>++.>+.+++++++..+++.>++.>---.<<.+++.------.--------.>+.>>.

总之视觉效果很震撼。

Brainfuck 语言是一门极小的图灵完备语言。它只有八种有效字符:

操作符 作用
> 指针向右移一字节
< 指针向左移一字节
+ 当前指向字节的值加一
- 当前指向字节的值减一
, 请求一个输入,将其ASCII码存到当前指向字节
. 将当前指向字节的值作为ASCII码输出一个字符
[ 循环体开头,当前指向字节为0时退出循环
] 循环体结尾

其他任何字符都视为注释。看到这里你应该能想象出这种代码会有多么折磨了。

在 macOS 上,若你有 Homebrew 环境,你可以用这条指令安装一个 Brainfuck 解释器:

$ brew install brainfuck

该解释器的实现来自于这个 GitHub 仓库

例程入门

你可以想象一个指针指向一排格子,每个格子内的数字都要在0~255之间。最开始指针指向最左边第一个格子,当收到指令>时,指针向右跑去第二个格子;再收到指令<时,指针向左跑回第一个格子。

现在写一份代码:

++++++++++++++++++++++++++++++++++++++++++++++++.

在第一格里作连续48个+,最后用一个.将这个值对应的 ASCII 字符输出。这段代码的作用就是输出一个字符「0」,也就是 ASCII 码值为48的字符。

将代码文件保存成随便什么文件名,比如first_program.bf。使用下面的命令把这个文件传给解释器就可以运行了:

$ brainfuck first_program.bf

输出如下:

0

顺带一提,「0」的后面连换行符都没有。如果你在用 bash,下一个命令提示符会直接跟在0后面;如果你和我一样在用 zsh,默认配置下会有一个莫名其妙的百分号%跟在后面,并自动帮你换了一行。

如果你懒得为这种事写个文件,这个解释器支持你直接把代码在终端里作为参数传给它,用-e关键字就好:

$ brainfuck -e "++++++++++++++++++++++++++++++++++++++++++++++++."

连续48个+太不方便了,而且有可能数错。你刚才可能和我一样偷了个懒,只打了六个加号,然后复制粘贴直到总共八遍。这其实是个好思路,把48拆成6乘8来实现,就可以减少许多加号。换成 Brainfuck 来写就是把6加八遍,或者把8加六遍。我们用前者:

++++++++[>++++++<-]>.

解释一下这段代码:

  1. ++++++++在第一格里放8;
  2. [看一下当前格(第一格)归零了没有。如果没有,开始循环:
    1. >右移到第二格;
    2. ++++++给第二格加上6;
    3. <左移到第一格;
    4. -给第一格减去1;
  3. ]回到第2步;
  4. >右移到第二格;
  5. .把第二格里这个字符输出。也即输出 ASCII 码为48的字符,也就是「0」。

Brainfuck 会把其他文字都当注释,所以要是看不懂,你可以多写点注释多换些行。

来写 Hello World!

想想就很折磨,太棒了!

最简单的实现方式就是直接去查 ASCII 码表,然后在每一格里只用+填好字,然后输出。

但那样就太多加号了。相比那种,这里有稍微高级一点的实现,加号的数量比较好看:

++++++++++ 10遍
[
	>+++++++    第二格:加7
	>++++++++++ 第三格:加10
	>+++        第四格:加3
	>+++++++++  第五格:加9
	>+          第六格:加1
	<<<<<-      循环条件,第一格:减1
]         循环完成,二~六格为:70,100,30,90,10
>++.      第二格:72,输出:H
>+.       第三格:101,输出:e
+++++++.. 第三格:108,输出两遍:ll
+++.      第三格:111,输出:o
>++.      第四格:32,输出空格
>---.     第五格:87,输出W
<<.       第三格:还是111,输出o
+++.      第三格:114,输出r
------.   第三格:108,输出l
--------. 第三格:100,输出d
>+.       第四格:33,输出!
>>.       第六格:10,换行

我贴心地加了注释。如果你只是想要吓人,可以去掉注释写在一行里:

++++++++++[>+++++++>++++++++++>+++>+++++++++>+<<<<<-]>++.>+.+++++++..+++.>++.>---.<<.+++.------.--------.>+.>>.

GitHub 仓库里有个更短的 Helloworld 官方例程brainfuck/examples/hello-short.bf,套了一大堆循环,看起来很恐怖,如果有兴趣可以分析一下试试看。

另外这语言是可以写 Quine 程序的。毕竟它图灵完备,而且也能输出。太可怕了。