你相信吗?一个编程语言从头到尾只用到8个不同的符号,却能在理论上写出你能想象到的任何程序。听到这种描述,很多人第一反应是骗人的把戏,或者是只能拿来玩票的“玩具”。但计算机科学里有一条硬核结论:它和图灵机等价,是真正意义上的图灵完备。这个语言就是Brainfuck——一个刻意被设计到极简的深奥编程语言(esolang)。它不是为了工程效率而生的,但如果你想用最赤裸的方式看清计算机的底层工作方式,它简直就是一堂浓缩到极致的内存管理实战课。
了解Brainfuck之前,得先把我们脑子中“高级语言”的滤镜摘掉。它没有变量名、没有函数、没有对象,甚至连加法运算都没有现成的操作符。整台“虚拟机”只有两个家伙:一根长度通常为30000个单元的内存磁带,每个单元初始都是0,只能存0到255的数值;还有一根数据指针(DP),刚开始指向第1个单元。你的全部程序,就是排列一堆单字符命令,指挥指针在磁带上左右移动,把某个单元里的数值加1、减1、读一个字符、写一个字符。听起来简陋到可笑,但就是这种近乎物理机器的抽象,让每一个学过它的人再回头看C语言指针、看汇编栈帧,都有一种瞬间通透的感觉。
八条命令,没有任何冗余。为了方便你一次看透,我把它们按职责拆成三组。
第一组:指针移动> 数据指针右移一格。< 数据指针左移一格。
这两个操作把所有内存操作都变成了“走到正确的位置再动手”的体力活。没有数组下标,全靠一步一步挪,你会亲身体验到内存寻址这件事到底有多原始。
第二组:数据修改与输入输出+ 把指针指向的单元数值加1。- 把指针指向的单元数值减1。. 把当前单元的数值当作ASCII码,输出对应的字符。, 读入一个字节,把它的ASCII码存进当前单元。
你没看错,加减就是全部算数。要想完成乘法、除法,全靠循环一层层垒出来。打印一个字母“A”,不是简单写一句print,而是要把某个单元的值精确地折腾到65,然后输出。这种把简单操作组合出一切的方式,就是Brainfuck教给你的第一课。
第三组:循环控制[ 如果当前单元的值是0,跳转到匹配的 ] 之后。] 如果当前单元的值不是0,跳转回匹配的 [ 之后。
这对括号就是Brainfuck唯一的控制结构,本质上是一个while循环。整个语言的艺术全在于此:你能不能把某个单元当计数器,在循环里不断地去另一个单元上做加减,以完成更复杂的计算,就看你对这段代码流程的把握。用不好,就只能在0和1之间打转;用好了,你可以构造出乘法、除法、条件判断,甚至调用子程序。也正是因为循环全靠手工编排,你没法偷懒,必须把每一步内存变化在脑子里跑一遍。
真正尝到舌头打结的滋味,还得看经典的Hello World程序。要输出字符“H”,你得先把一个单元折腾到72,接着输出;再折腾到101输出“e”……可问题来了,+一次只能加1,想从0加到72,那得敲72个+。这显然不现实。Brainfuck程序员就想出了“循环乘法”的套路:先在一个单元(计数器)里存5,然后进入循环,每循环一次,移动到右侧某个单元里加3,再移回来把计数器减1。循环跑完5趟,右侧单元里就被加出15。也就是说用5个+配合一个循环,就达到了5×3的效果。利用不同计数器和加法值的组合,就能高效地造出像72、101这种大数。
下面这段看似乱码的东西,就是完成一切魔术的完整程序:
++++++++[>++++[>++>+++>+++&
热门跟贴