Brainfuck Hello World如何实际运作?

spe*_*der 111 esoteric-languages brainfuck

有人把它寄给我,并声称这是Brainfuck的一个问候世界(我希望如此......)

++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.
Run Code Online (Sandbox Code Playgroud)

通过移动指针并递增和递减东西,我知道它的基本功能......

但我仍然想知道它是如何运作的?它首先如何在屏幕上打印任何内容?它如何编码文本?我根本不明白......

Sco*_*ony 236

1.基础知识

要了解Brainfuck,您必须想象0每个单元初始化的无限数组.

...[0][0][0][0][0]...
Run Code Online (Sandbox Code Playgroud)

当brainfuck程序启动时,它指向任何单元格.

...[0][0][*0*][0][0]...
Run Code Online (Sandbox Code Playgroud)

如果向右>移动指针,则将指针从单元格X移动到单元格X + 1

...[0][0][0][*0*][0]...
Run Code Online (Sandbox Code Playgroud)

如果你增加单元格值,+你得到:

...[0][0][0][*1*][0]...
Run Code Online (Sandbox Code Playgroud)

如果再次增加单元格值,+则会得到:

...[0][0][0][*2*][0]...
Run Code Online (Sandbox Code Playgroud)

如果您降低单元格值,-您会得到:

...[0][0][0][*1*][0]...
Run Code Online (Sandbox Code Playgroud)

如果向左<移动指针,则将指针从单元格X移动到单元格X-1

...[0][0][*0*][1][0]...
Run Code Online (Sandbox Code Playgroud)

2.输入

要阅读角色,请使用逗号,.它的作用是:从标准输入读取字符并将其十进制ASCII码写入实际单元格.

看看ASCII表.例如,十进制代码!33,a而是97.

好吧,让我们想象你的BF程序内存如下:

...[0][0][*0*][0][0]...
Run Code Online (Sandbox Code Playgroud)

假设标准输入代表a,如果你使用逗号,运算符,BF做的是读取a十进制ASCII代码97到内存:

...[0][0][*97*][0][0]...
Run Code Online (Sandbox Code Playgroud)

你通常想要那样思考,但真相有点复杂.事实是BF不读取字符而是读取字节(无论该字节是什么).让我举个例子:

在linux中

$ printf ?
Run Code Online (Sandbox Code Playgroud)

打印:

?
Run Code Online (Sandbox Code Playgroud)

这是特定的抛光特征.此字符不是由ASCII编码编码的.在这种情况下,它是UTF-8编码,因此它曾经在计算机内存中占用多个字节.我们可以通过制作十六进制转储来证明它:

$ printf ? | hd
Run Code Online (Sandbox Code Playgroud)

这表现了:

00000000  c5 82                                             |..|
Run Code Online (Sandbox Code Playgroud)

零被抵消.82是第一个并且c5是第二个字节代表?(按顺序我们将读取它们).|..|是图形表示,在这种情况下是不可能的.

好吧,如果你?作为输入传递给读取单字节的BF程序,程序存储器将如下所示:

...[0][0][*197*][0][0]...
Run Code Online (Sandbox Code Playgroud)

为什么197?那么197十进制为c5十六进制.似乎很熟悉?当然.这是第一个字节?!

3.输出

要打印字符,请使用dot .它的作用是:假设我们将实际单元格值视为十进制ASCII代码,将相应字符打印到标准输出.

好吧,让我们想象你的BF程序内存如下:

...[0][0][*97*][0][0]...
Run Code Online (Sandbox Code Playgroud)

如果你现在使用点(.)运算符,BF会做什么打印:

一个

因为aASCII中的十进制代码是97.

所以例如像这样的BF程序(97加2点):

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

将它指向的单元格值增加到97并将其打印出来2次.

AA

循环

BF循环由循环开始[和循环结束组成].您可以认为它就像在C/C++中,条件是实际的单元格值.

看看下面的BF程序:

++[]
Run Code Online (Sandbox Code Playgroud)

++ 两次增加实际单元格值:

...[0][0][*2*][0][0]...
Run Code Online (Sandbox Code Playgroud)

[]while(2) {},所以它的无限循环.

假设我们不希望这个循环是无限的.我们可以这样做:

++[-]
Run Code Online (Sandbox Code Playgroud)

因此每次循环循环时,它会减少实际的单元格值.一旦实际单元格值为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)
Run Code Online (Sandbox Code Playgroud)

让我们再看一下有限循环的另一个例子:

++[>]
Run Code Online (Sandbox Code Playgroud)

这个例子表明,我们没有在循环开始的单元格上完成循环:

...[0][0][*2*][0][0]...        loop starts
...[0][0][2][*0*][0]...        after first iteration (loop ends)
Run Code Online (Sandbox Code Playgroud)

但是,最好结束我们开始的地方.为什么?因为如果循环结束它开始的另一个单元格,我们就不能假设单元格指针在哪里.说实话,这种做法使脑力劳动减少了脑力.

  • 对于试图理解这种语言意识形态的新手来说,这是一个完美的解决方案.恭喜,伟大的帖子. (24认同)
  • 很酷,现在我明白了:) (4认同)
  • 最好的Brainfuck介绍我见过.老实说,你的帖子有点撤消BF (4认同)
  • 我想如果您需要一个空闲时间的项目,您可以随时向Brainfuck添加Unicode支持. (3认同)
  • 你的帖子之后,BF就是!BF了! (3认同)

ken*_*ken 50

维基百科有一个评论版的代码.

+++++ +++++             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'
Run Code Online (Sandbox Code Playgroud)

要回答您的问题,,.字符用于I/O. 文字是ASCII.

维基百科的文章接着在一些更深入,以及.

第一行a[0] = 10通过简单地从0递增十次来初始化.第2行的循环有效地设置了数组的初始值:( a[1] = 70接近72,字符'H'的ASCII代码),a[2] = 100(接近101或'e' ),a[3] = 30(接近32,空间代码)和a[4] = 10(换行符).循环的工作原理是7,10,3,和1,细胞增加a[1],a[2],a[3]a[4]分别各一次通过循环-在总(给予10添加用于每个小区 a[1]=70等).循环结束后,a[0]为零.>++.然后将指针移动到a[1],它保持70,向它添加两个(产生72,这是大写H的ASCII字符代码),并输出它.

下一行将数组指针移动到a[2]并向其添加一个,产生101,小写的"e",然后输出.

由于'l'恰好是'e'之后的第七个字母,输出'll'时,另外七个被添加(+++++++),a[2]结果输出两次.

'o'是'l'之后的第三个字母,因此a[2]再增加三次并输出结果.

程序的其余部分以相同的方式继续.对于空格和大写字母,选择不同的阵列单元并根据需要递增或递减.

  • 它打印是因为编译器知道`,`和`.`用于I/O,就像使用`putchar`的C打印一样.它是由编译器处理的实现细节. (8认同)

小智 8

为了回答它如何知道打印内容的问题,我在打印发生的代码右侧添加了ASCII值的计算:

> 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)
Run Code Online (Sandbox Code Playgroud)


The*_*hal 8

Brainfuck 和它的名字一样.它只使用8个字符> [ . ] , - +,这使它成为学习中最快的编程语言,但却难以实现和理解. ....并且最终让你最终得到你的大脑.

它将值存储在数组中:[72] [101] [108] [111]

let,最初指针指向数组的单元格1:

  1. > 将指针向右移动1

  2. < 将指针向左移动1

  3. + 将单元格的值增加1

  4. - 将元素的值增加1

  5. . 当前单元格的打印值.

  6. , 将输入输入当前单元格.

  7. [ ] 循环,+++ [ - ]计数器3计数bcz它前面有3'+', - 减少计数变量1值.

存储在单元格中的值是ascii值:

所以参考上面的数组:[72] [101] [108] [108] [111]如果你匹配ascii值,你会发现它是Hello writtern

恭喜!你已经学会了BF的语法

--- 更多 ---

让我们制作我们的第一个程序,即Hello World,之后您就可以用这种语言编写您的名字了.

+++++ +++++[> +++++ ++ >+++++ +++++ >+++ >+ <<<-]>++.>+.+++++ ++..+++.++.+++++ +++++ +++++.>.+++.----- -.----- ---.>+.>.
Run Code Online (Sandbox Code Playgroud)

分成几块:

+++++ +++++[> +++++ ++ 
                  >+++++ +++++ 
                  >+++ 
                  >+ 
                  <<<-]
Run Code Online (Sandbox Code Playgroud)

制作一个包含4个单元格的数组(数量为>)并设置一个10的计数器,如: - psuedo code--

array =[7,10,3,1]
i=10
while i>0:
 element +=element
 i-=1
Run Code Online (Sandbox Code Playgroud)

因为计数器值存储在单元格0中并且>移动到单元格1更新其值+ 7>移动到单元格2将10增加到其先前值,依此类推....

<<< 返回单元格0并将其值减1

因此在循环完成后我们有数组:[70,100,30,10]

>++. 
Run Code Online (Sandbox Code Playgroud)

移动到第一个元素并将其值增加2(两个'+'),然后使用该ascii值打印('.')字符.即例如在python中:chr(70 + 2)#打印'H'

>+.
Run Code Online (Sandbox Code Playgroud)

移动到第二个单元格增量1到它的值100 + 1并打印('.')它的值,即chr(101)chr(101)#prints'e'现在没有>或<in next piece,所以它取现值最新元素和增量只有它

+++++ ++..
Run Code Online (Sandbox Code Playgroud)

最新元素= 101因此,101 + 7并打印两次(因为有两个'..')chr(108)#prints l两次可以用作

for i in array:
    for j in range(i.count(‘.’)):
           print_value
Run Code Online (Sandbox Code Playgroud)

--- 它在哪里使用?---

它只是一种用来挑战程序员的笑话语言,并没有在任何地方使用.


小智 5

所有答案都很详尽,但缺少一个微小的细节:打印。在构建 Brainfuck 翻译器时,您还需要考虑角色.,这实际上就是 Brainfuck 中打印语句的样子。所以你的大脑翻译器应该做的是,每当它遇到一个.字符时,它就会打印当前指向的字节。

例子:

假设你有 --> char *ptr = [0] [0] [0] [97] [0]...如果这是一个 Brainfuck 语句: >>>.你的指针应该移动 3 个空格到右侧着陆点:[97],所以现在*ptr = 97,在你的翻译器遇到 a 之后.,它应该调用

write(1, ptr, 1)
Run Code Online (Sandbox Code Playgroud)

或任何等效的打印语句来打印当前指向的字节,其值为97,然后该字母a将被打印在std_output.