有人把这个发给我,并声称这是Brainfuck的一个问候世界(我希望如此......)
++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.
通过移动指针并递增和递减东西,我知道它的基本功能......
然而我仍然想知道它是如何运作的?它首先如何在屏幕上打印任何内容?它如何编码文本?我根本不明白......
答案 0 :(得分:224)
要了解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 table。例如,!
的十进制代码为33
,而a
为97
。
好吧,让我们想象你的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
十六进制。似乎很熟悉?当然。它是ł
的第一个字节!
要打印字符,请使用点.
它的作用是:假设我们将实际单元格值视为十进制ASCII代码,请将相应字符打印到标准输出。
好吧,让我们想象你的BF程序内存如下:
...[0][0][*97*][0][0]...
如果你现在使用点(。)运算符,BF会做什么打印:
一
因为ASCII中的a
十进制代码是97
。
所以例如像这样的BF程序(97加2点):
+++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++ ..
将它指向的单元格值增加到97并将其打印出来2次。
AA
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
+
将单元格的值增加1
-
将元素的值增加1
.
当前单元格的打印值。
,
接受当前单元格的输入。
[ ]
循环,+++ [ - ]计数器3计数bcz它前面有3'+',并且 - 将计数变量减1值。
存储在单元格中的值是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