Brainfuck Hello Worldは実際どのように機能しますか?


118

誰かがこれを私に送り、それがBrainfuckのHello Worldだと主張しました(そして私はそう願っています...)

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

私はそれがポインターを動かし、ものをインクリメントおよびデクリメントすることによってそれが機能するという基本を知っています...

それでも知りたいのですが、実際にはどのように機能しますか?そもそもどうやって画面に何かを印刷するのですか?テキストをどのようにエンコードしますか?全然わからない…


31
この言語で記述されたアプリケーションを維持するのはかなり難しい必要があります..
e2-e4 2013年

17
@ ring0:いや、それは書き込み専用言語です。
LetMeSOThat4U 2015年

何が実用的ですか?
Yash Kumar Verma

10
@YashVerma必要ありません。–
非常識

49
@YashVerma言語名で明記されています。
Mateen Ulhaq 2016

回答:


255

1.基本

Brainfuckを理解するには、0それぞれによって初期化されるセルの無限配列を想像する必要があります。

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

ブレインファックプログラムが開始すると、任意のセルを指します。

...[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.入力

文字を読むにはコンマを使います ,。機能:標準入力から文字を読み取り、その10進ASCIIコードを実際のセルに書き込みます。

ASCIIテーブルを見てください。たとえば、次の10進コード!33、while a97です。

まあ、あなたのBFプログラムのメモリが次のようになっていると想像してみましょう:

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

標準入力がを表すとするとa、コンマ,演算子を使用する場合、BFが実行するのはa10進ASCIIコード97をメモリに読み込むことです。

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

一般的にそのように考えたいと思いますが、真実はもう少し複雑です。真実は、BFは文字ではなくバイトを読み取ることです(そのバイトが何であっても)。例を示しましょう:

Linuxでは

$ printf ł

プリント:

ł

特定のポリッシュ文字です。この文字はASCIIエンコードではエンコードされません。この場合は、UTF-8エンコードであるため、コンピュータのメモリで1バイト以上を使用していました。16進ダンプを作成することでそれを証明できます。

$ printf ł | hd

示す:

00000000  c5 82                                             |..|

ゼロはオフセットされます。82最初のc5バイトと2番目のバイトを表しますł(これらを読み取るため)。|..|この場合は不可能なグラフィック表現です。

さて、ł1バイトを読み取るBFプログラムに入力として渡すと、プログラムメモリは次のようになります。

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

なんで197?まあ19710進数はc516進数です。見覚えがある?もちろん。の最初のバイトですł

3.出力

文字を印刷するには、ドットを使用します。.機能は次のとおりです。実際のセル値を10進ASCIIコードのように扱うと仮定して、対応する文字を標準出力に出力します。

まあ、あなたのBFプログラムのメモリが次のようになっていると想像してみましょう:

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

ここでドット(。)演算子を使用する場合、BFは何を印刷するかを指定します。

a

そのためaASCIIで10進数です97

たとえば、次のようなBFプログラム(97プラス2ドット):

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

最大97を指すセルの値を増やし、2回出力します。

ああ

4.ループ

BFでは、ループはループ開始[とループ終了で構成されます]。条件が実際のセル値であるC / C ++のようなものだと考えることができます。

以下のBFプログラムを見てください。

++[]

++ 実際のセル値を2回インクリメントします。

...[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)

ただし、開始した場所を終了することをお勧めします。どうして ?ループが開始した別のセルを終了する場合、セルポインターがどこにあるかは想定できません。正直に言うと、この慣習により、brainfuckは少なくなります。


4
クール、今私はそれを理解しました:)
スピーダー

25
これは、初心者がこの言語のイデオロギーを理解しようとするのに最適なソリューションでした。おめでとう、そして素晴らしい投稿。
ケーシー

4
私が見た中で最高のBrainfuckイントロ。正直なところ、あなたの投稿によってBFを少し元に戻します
Boyang

3
余暇にプロジェクトが必要な場合は、いつでもBrainfuckにUnicodeサポートを追加できると思います。
アルバロ・ゴンサレス

3
あなたの投稿の後、BFはもう!BFです!
thanos.a 2017

52

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、0から10回インクリメントするだけで初期化されます。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を保持し、それに2を加算し(72を生成します。これは大文字のHのASCII文字コードです)、それを出力します。

次の行は、配列ポインターをa[2]それに移動して1を追加し、小文字の「e」である101を生成して出力します。

「l」は「e」の後ろの7番目の文字であるため、「ll」を出力するには、さらに7つが追加され(+++++++a[2]、結果が2回出力されます。

「o」は「l」に続く3番目の文字なので、a[2]さらに3回インクリメントされ、結果が出力されます。

プログラムの残りの部分も同じように続きます。スペースと大文字の場合、異なる配列セルが選択され、必要に応じて増分または減分されます。


しかし、なぜそれが印刷されるのですか?またはどうやって?コメントはラインの意図を私に説明しています、今それが何をするか。
スピーダー、2013年

8
Cがを使用して印刷するのと同じように、コンパイラーがそれ,を認識して.入出力に使用するため、印刷されputcharます。これは、コンパイラーによって処理される実装の詳細です。
ケン

1
また、「Hello World」のASCII文字に必要なセルを整数値に設定しているため
slugonamission

私はより多くの深さの説明を期待...しかし:/
スピーダー

1
@speeder-ウィキペディアからのコードの詳細な説明を回答に追加しました。詳細については、リンクされた記事を参照してください。
ケン

9

何を印刷するかを知る方法の質問に答えるために、印刷が行われるコードの右側に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)

9

その名前と同じBrainfuck。使用するのは8文字だけな> [ . ] , - +ので、学習するのに最も速いプログラミング言語ですが実装して理解するの最も困難です。 …。そして、あなたは最終的にあなたの脳を騙すことになります。

配列に値を格納します:[72] [101] [108] [111]

まず、配列のセル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を作成してみましょう。その後、この言語で名前を書くことができます。

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

バラバラに:

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

4セル(>の数)の配列を作成し、10のカウンターを次のように設定します。—-psuedo code—-

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

カウンタ値はセル0に格納され、>セル1に移動すると、その値が+7に更新されます。>セル2に移動すると、前の値に10ずつ増加します。

<<< セル0に戻り、その値を1だけ減らす

したがって、ループの完了後、配列は次のようになります:[70,100,30,10]

>++. 

最初の要素に移動し、その値を2(2つの '+')だけインクリメントしてから、そのASCII値で文字を印刷( '。')します。つまり、たとえばpythonの場合:chr(70 + 2)# 'H'を出力します

>+.

2番目のセルのインクリメント1に移動し、その値を100 + 1にして、その値を出力します。最新の要素とその増分のみ

+++++ ++..

したがって、最新の要素= 101、つまり101 + 7で2回出力されます(2 '..'があるため)chr(108)#prints lは次のように使用できます

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

——— それはどこで使用されますか?——-

これはプログラマーに挑戦するために作られた単なる冗談言語であり、実際にはどこでも使用されていません。


4

すべての答えは完全ですが、1つの細かい詳細が欠けています:印刷。ブレインファックトランスレータを構築する際には、キャラクターも考慮します.。これは、実際にはブレインファックでの印刷ステートメントのようです。つまり、brainfuckトランスレータが行うべきことは、.文字に遭遇するたびに、現在指しているバイトを出力することです。

例:

-> char *ptr = [0] [0] [0] [97] [0]...これがブレインファックのステートメントで あると仮定します>>>.。ポインタを3スペース右に移動する必要があります:[97]なので*ptr = 97、翻訳者がに遭遇した.後、

write(1, ptr, 1)

または、現在ポイントされているバイトを印刷する同等の印刷ステートメント。値が97で、文字aがに印刷されますstd_output


1

私はあなたが求めているのは、Brainfuckがどのようにすべてのコードをどう処理するかを知っているかだと思います。Pythonのような高水準言語で記述されたパーサーがあり、コード内のドットの意味や追加記号の意味を解釈しています。

したがって、パーサーはコードを1行ずつ読み取り、>記号があるので、メモリの場所を進める必要があります。コードは、(そのメモリの場所の内容)==>、memlocation = + memlocationの場合、単純です同様に(メモリロケーションのコンテンツ)== "。"の場合、より高いレベルの言語で記述され、(メモリロケーションのコンテンツ)を出力します。

これで問題が解決することを願っています。tc

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.