Brainfuck Hello World如何实际运作?

时间:2013-05-30 12:57:05

标签: brainfuck esoteric-languages

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

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

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

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

6 个答案:

答案 0 :(得分:224)

1。基本

要了解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]...

2。输入

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

看看ASCII table。例如,!的十进制代码为33,而a97

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

...[0][0][*0*][0][0]...

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

...[0][0][*97*][0][0]...

你通常想要这样想,但事实上要复杂一点。事实是BF不读取字符而是读取字节(无论该字节是什么)。让我举个例子:

在linux中

$ printf ł

打印:

ł

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

$ printf ł | hd

显示:

00000000  c5 82                                             |..|

零偏移。 82是第一个,c5是代表ł的第二个字节(按顺序我们会读取它们)。 |..|是图形表示,在这种情况下是不可能的。

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

...[0][0][*197*][0][0]...

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

3。输出

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

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

...[0][0][*97*][0][0]...

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

  

因为ASCII中的a十进制代码是97

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

  

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

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

  

AA

4。环

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

看看下面的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)

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

答案 1 :(得分:48)

Wikipedia有一个注释版本的代码。

+++++ +++++             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'

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

Wikipedia文章也有更深入的内容。

  

第一行通过简单地递增十次来初始化a[0] = 10   来自第2行的循环有效地设置了初始值   数组:a[1] = 70(接近72,字符的ASCII码   'H'),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',然后输出。

     

因为'我'发生了   成为'e'之后的第七个字母,输出'll'另外七个字母   将(+++++++)添加到a[2],结果输出两次。

     

'o'是   'l'之后的第三个字母,所以a[2]再增加三次   输出结果。

     

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

答案 2 :(得分: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)

答案 3 :(得分:7)

<强> Brainfuck 与其名称相同。 它仅使用8个字符> [ . ] , - +,这使其成为最快编程语言学习,但最难实现理解。 ....并且让你最终得到你的大脑。

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

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

  1. >将指针向右移动1

  2. <将指针向左移动1

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

  4. -将元素的值增加1

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

  6. ,接受当前单元格的输入。

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

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

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

    恭喜!您已经学习了BF

    的语法

    --- 更多 ---

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

    +++++ +++++[> +++++ ++ >+++++ +++++ >+++ >+ <<<-]>++.>+.+++++ ++..+++.++.+++++ +++++ +++++.>.+++.----- -.----- ---.>+.>.
    
    分成几块:

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

    制作一个包含4个单元格的数组(数量为&gt;)并设置10个计数器,例如: --psuedo代码 -

    array =[7,10,3,1]
    i=10
    while i>0:
     element +=element
     i-=1
    

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

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

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

    >++. 
    

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

    >+.
    

    移动到第二个单元格增量1到其值100 + 1并打印('。')其值,即chr(101) chr(101)#prints'e' 现在没有&gt;或者&lt;在下一篇文章中,它只取最新元素的现值并仅增加

    +++++ ++..
    

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

    for i in array:
        for j in range(i.count(‘.’)):
               print_value
    

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

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

答案 4 :(得分:3)

所有答案都很全面,但缺少一个细节:打印。 在构建你的brainfuck翻译器时,你还要考虑字符.,这实际上是打印语句在brainfuck中的样子。那么你的脑筋翻译应该做的是,每当遇到.字符时,它就会打印当前指向的字节。

示例:

假设你有 - &gt; char *ptr = [0] [0] [0] [97] [0] ... 如果这是一个brainfuck声明:>>>.你的指针应该移动3个空格到右边的着陆位置:[97],所以现在*ptr = 97,这样你的翻译就会遇到. ,然后应该调用

write(1, ptr, 1)

或打印当前指向字节的任何等效打印语句,其值 97 ,然后字母a将打印在std_output上。

答案 5 :(得分:1)

我认为您要问的是Brainfuck如何知道如何处理所有代码。有一个用Python等高级语言编写的解析器,用于解释点的含义或代码中加号的含义。

因此,解析器将逐行读取您的代码,并说好,那里有一个>符号,因此我必须提前存储位置,代码很简单,如果(该存储位置中的内容)==>,内存分配= +使用更高级别语言编写的内存分配,类似地,如果(内存位置的内容)==“。”,则打印(内存位置的内容)。

希望这可以清除它。 tc