有人把这个发送给我,并声称这是Brainfuck语言的Hello World(我希望是这样...)
++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.
我知道它的基本原理是通过移动指针并增减数据来工作...
但我仍然想知道,它到底是如何工作的?它是如何在屏幕上打印任何东西的?它是如何编码文本的?我完全不理解...
有人把这个发送给我,并声称这是Brainfuck语言的Hello World(我希望是这样...)
++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.
我知道它的基本原理是通过移动指针并增减数据来工作...
但我仍然想知道,它到底是如何工作的?它是如何在屏幕上打印任何东西的?它是如何编码文本的?我完全不理解...
要理解Brainfuck,你需要想象一个由0
初始化的无限单元格数组。
...[0][0][0][0][0]...
当brainfuck程序启动时,它会指向任何一个单元格。
...[0][0][*0*][0][0]...
如果将指针向右移动>
,就是将指针从单元格X移动到单元格X+1。
...[0][0][0][*0*][0]...
如果增加单元格数值+
,你将得到:
...[0][0][0][*1*][0]...
如果您再次增加单元格的值+
,您将得到:
...[0][0][0][*2*][0]...
-
,你会得到:...[0][0][0][*1*][0]...
<
,那么你就是将指针从单元格 X 移动到单元格 X-1。...[0][0][*0*][1][0]...
要读取字符,您需要使用逗号 ,
。它的作用是:从标准输入中读取字符,并将其十进制 ASCII 码写入实际单元格。
请参考 ASCII 表。例如,!
的十进制码是 33
,而 a
的十进制码是 97
。
好的,现在想象一下您的BF程序内存看起来像:
...[0][0][*0*][0][0]...
假设标准输入代表 a
,如果您使用逗号 ,
运算符,则BF会将十进制ASCII码 97
读入内存:
...[0][0][*97*][0][0]...
$ printf ł
输出:
ł
这是一个特定的波兰字符,无法由 ASCII 编码表示。在此情况下使用 UTF-8 编码,因此它需要在计算机内存中占用多个字节。我们可以通过进行十六进制转储来证明:
$ printf ł | hd
这显示:
00000000 c5 82 |..|
82
,第二个字节是c5
,表示我们要读取的字符为ł
。其中|..|
是不可能在这种情况下出现的图形化表示。ł
作为输入传递给只读取单个字节的BF程序,则程序内存将如下所示:...[0][0][*197*][0][0]...
为什么是197
?因为十进制的197
等于十六进制的c5
。感觉很熟悉吗?当然了。它就是字母ł
的第一个字节!
要打印字符,你需要使用点号.
。它的作用是:假设我们把实际单元格的值视为十进制ASCII码,将相应的字符打印到标准输出。
好了,现在让我们假设你的BF程序内存看起来像这样:
...[0][0][*97*][0][0]...
a
因为 ASCII 中的a十进制代码是 97
。
例如,BF程序如下(97个加号和2个点):
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..
将使所指向的单元格的值增加到97,并将其打印出来两次。
aa
在 BF 中,循环由循环开始符号 [
和循环结束符号 ]
组成。您可以将其视为 C/C++ 中的 while,其中条件是实际单元格值。
请看下面的 BF 程序:
++[]
++
会使实际单元格的值增加两次:
...[0][0][*2*][0][0]...
[]
就像 while(2) {}
,所以它是一个无限循环。
假设我们不想让这个循环无限制地执行下去,我们可以采取以下措施:
++[-]
0
,循环就会结束。...[0][0][*2*][0][0]... loop starts
...[0][0][*1*][0][0]... after first iteration
...[0][0][*0*][0][0]... after second iteration (loop ends)
让我们再考虑一个有限循环的例子:
++[>]
...[0][0][*2*][0][0]... loop starts
...[0][0][2][*0*][0]... after first iteration (loop ends)
维基百科有一份注释版的代码。
+++++ +++++ initialize counter (cell #0) to 10
[ use loop to set the next four cells to 70/100/30/10
> +++++ ++ add 7 to cell #1
> +++++ +++++ add 10 to cell #2
> +++ add 3 to cell #3
> + add 1 to cell #4
<<<< - decrement counter (cell #0)
]
> ++ . print 'H'
> + . print 'e'
+++++ ++ . print 'l'
. print 'l'
+++ . print 'o'
> ++ . print ' '
<< +++++ +++++ +++++ . print 'W'
> . print 'o'
+++ . print 'r'
----- - . print 'l'
----- --- . print 'd'
> + . print '!'
> . print '\n'
,
和.
字符用于输入/输出。文本采用ASCII编码。
维基百科文章也进一步深入讨论了这个话题。a [0] = 10
初始化为从0开始递增十次。第2行的循环有效地设置了数组的初始值:a [1] = 70
(接近字符“H”的ASCII代码72),a [2] = 100
(接近字符“e”的101或'e'),a [3] = 30
(接近空格的代码32)和a [4] = 10
(换行符)。循环通过在每次循环中分别向单元格a [1]
、a [2]
、a [3]
和a [4]
添加7、10、3和1来工作,总共对每个单元格进行10次加法运算(得到a [1]=70
等)。循环结束后,a [0]
为零。>++。
然后将指针移动到a [1]
,其值为70,将其加上2(产生72,即大写字母H的ASCII字符代码),并输出它。a [2]
并将其加1,生成101,即小写字母'e',然后输出。+++++++
)添加到a [2]
中,然后将结果输出两次。a [2]
再增加三次并输出结果。Brainfuck,顾名思义。它只使用了 8 个字符> [ . ] , - +
,这使得它是最快速学习的编程语言,但也是最难实现和理解的编程语言。最终你会被逼疯。
它在数组中存储值:[72] [101] [108] [111]
初始化指针指向数组的第1个单元格:
>
将指针向右移动1个位置
<
将指针向左移动1个位置
+
将单元格的值加1
-
将单元格的值减1
.
输出当前单元格的值
,
输入数据到当前单元格
[ ]
循环,如 +++[-],表示循环3次,- 运算符将计数器的值减1
存储在单元格中的值是 ASCII 值:
因此,参考上面的数组:[72] [101] [108] [108] [111],如果你匹配 ASCII 值,你会发现它写的是 Hello。
恭喜!你已经学会了 BF 的语法。
——-更多内容———
让我们编写第一个程序,即 Hello World,之后你就能用这种语言写下自己的名字了。
+++++ +++++[> +++++ ++ >+++++ +++++ >+++ >+ <<<-]>++.>+.+++++ ++..+++.++.+++++ +++++ +++++.>.+++.----- -.----- ---.>+.>.
分解成若干部分:
+++++ +++++[> +++++ ++
>+++++ +++++
>+++
>+
<<<-]
array =[7,10,3,1]
i=10
while i>0:
element +=element
i-=1
<<<
返回到单元格0并将其值减1。>++.
移动到第一个元素并将其值增加2(两个'+'),然后使用该ASCII值打印('.')字符。例如在Python中:
chr(70+2) # 打印'H'
>+.
移动到第二个单元格,将其值增加1到101并打印('.')其值,即chr(101) chr(101)#打印'e' 现在下一段中没有>或<,因此它只取最新元素的当前值并将其递增
+++++ ++..
最新元素 = 101 因此,101 + 7 并将其打印两次(因为有两个“..”)chr(108)#打印l两次 可以用作
for i in array:
for j in range(i.count(‘.’)):
print_value
———它在哪里被使用?——-
这只是一种用于挑战程序员的玩笑语言,实际上没有被广泛应用。
> just means move to the next cell
< just means move to the previous cell
+ and - are used for increment and decrement respectively. The value of the cell is updated when the increment/decrement happens
+++++ +++++ initialize counter (cell #0) to 10
[ use loop to set the next four cells to 70/100/30/10
> +++++ ++ add 7 to cell #1
> +++++ +++++ add 10 to cell #2
> +++ add 3 to cell #3
> + add 1 to cell #4
<<<< - decrement counter (cell #0)
]
> ++ . print 'H' (ascii: 70+2 = 72) //70 is value in current cell. The two +s increment the value of the current cell by 2
> + . print 'e' (ascii: 100+1 = 101)
+++++ ++ . print 'l' (ascii: 101+7 = 108)
. print 'l' dot prints same thing again
+++ . print 'o' (ascii: 108+3 = 111)
> ++ . print ' ' (ascii: 30+2 = 32)
<< +++++ +++++ +++++ . print 'W' (ascii: 72+15 = 87)
> . print 'o' (ascii: 111)
+++ . print 'r' (ascii: 111+3 = 114)
----- - . print 'l' (ascii: 114-6 = 108)
----- --- . print 'd' (ascii: 108-8 = 100)
> + . print '!' (ascii: 32+1 = 33)
> . print '\n'(ascii: 10)
。
,这实际上就是Brainfuck中打印语句的样子。因此,当您的Brainfuck翻译器遇到。
字符时,它应该打印当前指向的字节。char *ptr = [0] [0] [0] [97] [0]
...
如果这是一个Brainfuck语句:>>>.
,则您的指针应该向右移动3个空格,落在:[97]
,所以现在*ptr = 97
,完成后您的翻译器遇到。
,然后应该调用write(1, ptr, 1)
或者任何等效的打印语句来打印当前指向的字节,该字节的值为97,并且字母a
将被打印在std_output
上。