Wie funktioniert die Brainfuck Hello World eigentlich?

118

Jemand hat mir das geschickt und behauptet, es sei eine Hallo-Welt in Brainfuck (und ich hoffe es ...)

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

Ich kenne die Grundlagen, die es funktioniert, indem man einen Zeiger bewegt und Sachen inkrementiert und dekrementiert ...

Trotzdem möchte ich wissen, wie es eigentlich funktioniert. Wie druckt es überhaupt etwas auf dem Bildschirm? Wie verschlüsselt es den Text? Ich verstehe gar nichts...

Speeder
quelle
31
Es muss ziemlich schwierig sein, eine in dieser Sprache geschriebene Anwendung zu pflegen.
e2-e4
17
@ ring0: nein, das ist eine reine Schreibsprache.
LetMeSOThat4U
Was ist der praktische Nutzen?
Yash Kumar Verma
10
@ YashVerma es braucht keine ..
Wahnsinn
49
@YashVerma Es ist klar im Namen der Sprache angegeben.
Mateen Ulhaq

Antworten:

255

1. Grundlagen

Um Brainfuck zu verstehen, müssen Sie sich eine unendliche Anzahl von Zellen vorstellen, die von 0jeder initialisiert wurden .

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

Wenn das Brainfuck-Programm gestartet wird, zeigt es auf eine beliebige Zelle.

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

Wenn Sie den Zeiger nach rechts bewegen, bewegen >Sie den Zeiger von Zelle X zu Zelle X + 1

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

Wenn Sie den Zellenwert erhöhen, erhalten +Sie:

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

Wenn Sie den Zellenwert erneut erhöhen, erhalten +Sie:

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

Wenn Sie den Zellenwert verringern, erhalten -Sie:

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

Wenn Sie den Zeiger nach links bewegen, bewegen <Sie den Zeiger von Zelle X zu Zelle X-1

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

2. Eingabe

Um Zeichen zu lesen, verwenden Sie Komma ,. Was es tut, ist: Lesen Sie das Zeichen von der Standardeingabe und schreiben Sie seinen dezimalen ASCII-Code in die eigentliche Zelle.

Schauen Sie sich die ASCII-Tabelle an . Zum Beispiel ist der Dezimalcode von !is 33, while ais 97.

Stellen wir uns vor, Ihr BF-Programmspeicher sieht folgendermaßen aus:

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

Angenommen, die Standardeingabe steht für a: Wenn Sie einen Kommaoperator verwenden ,, liest BF adezimalen ASCII-Code 97in den Speicher:

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

Sie möchten im Allgemeinen so denken, aber die Wahrheit ist etwas komplexer. Die Wahrheit ist, dass BF kein Zeichen, sondern ein Byte liest (was auch immer dieses Byte ist). Lassen Sie mich Ihnen ein Beispiel zeigen:

Unter Linux

$ printf ł

Drucke:

ł

Das ist spezifischer polnischer Charakter. Dieses Zeichen wird nicht durch ASCII-Codierung codiert. In diesem Fall handelt es sich um eine UTF-8-Codierung, sodass früher mehr als ein Byte im Computerspeicher benötigt wurde. Wir können es beweisen, indem wir einen hexadezimalen Dump erstellen:

$ printf ł | hd

welche Shows:

00000000  c5 82                                             |..|

Nullen werden versetzt. 82ist das erste und c5das zweite Byte ł(in der Reihenfolge, in der wir sie lesen werden). |..|ist eine grafische Darstellung, die in diesem Fall nicht möglich ist.

Wenn Sie łals Eingabe an Ihr BF-Programm übergeben, das ein einzelnes Byte liest, sieht der Programmspeicher folgendermaßen aus:

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

Warum 197? Nun, 197Dezimalzahl ist c5hexadezimal. Scheint vertraut ? Natürlich. Es ist das erste Byte von ł!

3. Ausgabe

So drucken Charakter Verwendung der Punkt .Was sie tut , ist: Angenommen , wir tatsächlichen Zellwert wie dezimal ASCII - Code behandeln, drucken Zeichen auf die Standardausgabe entspricht.

Stellen wir uns vor, Ihr BF-Programmspeicher sieht folgendermaßen aus:

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

Wenn Sie jetzt den Punkt (.) -Operator verwenden, druckt BF Folgendes:

ein

Weil aDezimalcode in ASCII ist 97.

So zum Beispiel ein BF-Programm wie dieses (97 plus 2 Punkte):

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

Erhöht den Wert der Zelle, auf die sie zeigt, auf bis zu 97 und druckt sie zweimal aus.

aa

4. Schleifen

In BF besteht die Schleife aus Schleifenanfang [und Schleifenende ]. Sie können denken, es ist wie in C / C ++, wo die Bedingung der tatsächliche Zellenwert ist.

Schauen Sie sich das BF-Programm unten an:

++[]

++ erhöht den tatsächlichen Zellenwert zweimal:

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

Und []ist wie while(2) {}, so ist es Endlosschleife.

Angenommen, wir möchten nicht, dass diese Schleife unendlich ist. Wir können zum Beispiel tun:

++[-]

Jedes Mal, wenn eine Schleife eine Schleife durchläuft, wird der tatsächliche Zellenwert verringert. Sobald der tatsächliche Zellenwert erreicht ist 0, endet die Schleife:

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

Betrachten wir noch ein weiteres Beispiel für eine endliche Schleife:

++[>]

Dieses Beispiel zeigt, dass wir die Schleife nicht in der Zelle beenden müssen, in der die Schleife begonnen hat:

...[0][0][*2*][0][0]...        loop starts
...[0][0][2][*0*][0]...        after first iteration (loop ends)

Es ist jedoch eine gute Praxis, dort zu enden, wo wir begonnen haben. Warum ? Denn wenn die Schleife eine andere Zelle beendet, die sie gestartet hat, können wir nicht davon ausgehen, wo sich der Zellenzeiger befindet. Um ehrlich zu sein, macht diese Praxis Brainfuck weniger Brainfuck.

Scony
quelle
4
Cool, jetzt habe ich es verstanden :)
Speeder
25
Das war eine perfekte Lösung für den Anfänger, der versuchte, diese Sprachideologie zu verstehen. Herzlichen Glückwunsch und tolle Post.
Casey
4
Das beste Brainfuck-Intro, das ich je gesehen habe. Ehrlich gesagt machst du BF ein bisschen durch deinen Beitrag rückgängig
Boyang
3
Ich denke, wenn Sie ein Projekt für Ihre Freizeit benötigen, können Sie Brainfuck jederzeit um Unicode-Unterstützung erweitern.
Álvaro González
3
Nach deinem Beitrag ist BF! BF mehr!
thanos.a
52

Wikipedia hat eine kommentierte Version des Codes.

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

Zur Beantwortung Ihrer Fragen, die ,und .sind Zeichen für I / O verwendet. Der Text ist ASCII.

Der Wikipedia- Artikel geht auch etwas tiefer.

Die erste Zeile wird a[0] = 10durch einfaches zehnmaliges Inkrementieren von 0 initialisiert . Die Schleife aus Zeile 2 legt effektiv die Anfangswerte für das Array fest: a[1] = 70(nahe 72, der ASCII-Code für das Zeichen 'H'), a[2] = 100(nahe 101 oder 'e'). ), a[3] = 30(nahe 32, der Code für Leerzeichen) und a[4] = 10(Zeilenumbruch). Die Schleife arbeitet durch Zugabe von 7, 10, 3 und 1, auf Zellen a[1], a[2], a[3]und a[4]jeweils jedes Mal durch die Schleife - 10 Additionen für jede Zelle insgesamt (Angabe a[1]=70etc.). Nachdem die Schleife beendet ist, a[0]ist Null. >++.Bewegt dann den Zeiger auf a[1], der 70 enthält, fügt zwei hinzu (wobei 72 erzeugt wird, was der ASCII-Zeichencode eines Großbuchstaben H ist) und gibt ihn aus.

Die nächste Zeile bewegt den Array-Zeiger auf a[2]und fügt einen hinzu, wodurch 101, ein Kleinbuchstaben 'e', ​​erzeugt wird, das dann ausgegeben wird.

Da 'l' zufällig der siebte Buchstabe nach 'e' ist, werden zur Ausgabe von 'll' weitere sieben hinzugefügt ( +++++++) a[2]und das Ergebnis wird zweimal ausgegeben.

'o' ist der dritte Buchstabe nach 'l', wird also a[2]noch dreimal erhöht und gibt das Ergebnis aus.

Der Rest des Programms läuft genauso ab. Für die Leerzeichen und Großbuchstaben werden verschiedene Array-Zellen ausgewählt und nach Bedarf inkrementiert oder dekrementiert.

Ken
quelle
Aber warum druckt es? oder wie? Die Kommentare erklären mir die Absicht der Linie, was sie jetzt tut.
Speeder
8
Es wird gedruckt, weil der Compiler das weiß ,und .für E / A verwendet wird, ähnlich wie C-Drucke mit putchar. Es ist ein Implementierungsdetail, das vom Compiler verarbeitet wird.
Ken
1
Und auch, weil es die erforderlichen Zellen auf die ganzzahligen Werte für die ASCII-Zeichen in "Hello World" setzt
slugonamission
Ich erwartete eine ausführlichere Erklärung ... aber: /
Speeder
1
@speeder - Ich habe der Antwort die ausführliche Erklärung des Codes aus Wikipedia hinzugefügt. Weitere Informationen finden Sie im verlinkten Artikel.
Ken
9

Um die Frage zu beantworten, woher es weiß, was gedruckt werden soll, habe ich die Berechnung der ASCII-Werte rechts neben dem Code hinzugefügt, in dem gedruckt wird:

> 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)
Rehana Mahfuz
quelle
9

Brainfuck wie sein Name. Es verwendet nur 8 Zeichen > [ . ] , - +, was es zur am schnellsten zu erlernenden Programmiersprache macht , aber am schwierigsten zu implementieren und zu verstehen. … Und bringt dich dazu, dein Gehirn zu ficken.

Es speichert Werte im Array: [72] [101] [108] [111]

Lassen Sie den Zeiger zunächst auf Zelle 1 des Arrays zeigen:

  1. > Bewegen Sie den Zeiger um 1 nach rechts

  2. < Bewegen Sie den Zeiger um 1 nach links

  3. + Erhöhen Sie den Wert der Zelle um 1

  4. - Erhöhen Sie den Wert des Elements um 1

  5. . Druckwert der aktuellen Zelle.

  6. , Eingabe in die aktuelle Zelle vornehmen.

  7. [ ] Schleife, +++ [-] Zähler mit 3 Zählungen, da 3 '+' vor ihm liegen, und - die Zählvariable um 1 Wert dekrementiert.

Die in Zellen gespeicherten Werte sind ASCII-Werte:

Beziehen Sie sich also auf das obige Array: [72] [101] [108] [108] [111] Wenn Sie mit den ASCII-Werten übereinstimmen, werden Sie feststellen, dass es sich um Hello writtern handelt

Glückwunsch! Sie haben die Syntax von BF gelernt

--- Etwas mehr ---

Lassen Sie uns unser erstes Programm machen, dh Hello World . Danach können Sie Ihren Namen in dieser Sprache schreiben.

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

in Stücke brechen:

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

Erstellt ein Array von 4 Zellen (Anzahl von>) und setzt einen Zähler von 10 wie: —-puedo code—-

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

Da der Zählerwert in Zelle 0 gespeichert ist und> in Zelle 1 verschoben wird, wird sein Wert um + 7 aktualisiert.> In Zelle 2 wird 10 auf den vorherigen Wert erhöht und so weiter.

<<< kehre zu Zelle 0 zurück und dekrementiere ihren Wert um 1

daher haben wir nach Abschluss der Schleife ein Array: [70,100,30,10]

>++. 

Bewegt sich zum ersten Element und erhöht seinen Wert um 2 (zwei '+') und druckt dann das Zeichen ('.') mit diesem ASCII-Wert. dh zum Beispiel in Python: chr (70 + 2) # druckt 'H'

>+.

bewegt sich zum 2. Zelleninkrement 1 auf seinen Wert 100 + 1 und druckt ('.') seinen Wert, dh chr (101) chr (101) #prints 'e', ​​jetzt gibt es kein> oder <im nächsten Stück, so dass es den aktuellen Wert annimmt des neuesten Elements und nur inkrementieren

+++++ ++..

Das neueste Element = 101 ist daher 101 + 7 und druckt es zweimal (da es zwei '..' gibt). chr (108) #prints l zweimal kann als verwendet werden

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

——— Wo wird es verwendet? ——-

Es ist nur eine Scherzsprache, die Programmierer herausfordert und praktisch nirgendwo verwendet wird.

DARK_C0D3R
quelle
4

Alle Antworten sind gründlich, aber es fehlt ihnen ein kleines Detail: Drucken. Beim Aufbau Ihres Brainfuck-Übersetzers berücksichtigen Sie auch den Charakter. So .sieht eine Druckaussage in Brainfuck aus. Ihr Brainfuck-Übersetzer sollte also immer dann, wenn er auf ein .Zeichen trifft, das aktuell angezeigte Byte drucken.

Beispiel:

Angenommen , Sie haben -> char *ptr = [0] [0] [0] [97] [0]... wenn dies eine Gehirnfick Anweisung lautet: >>>.Ihr Zeiger sollte 3 Plätze nach rechts Landung in verschoben werden: [97], jetzt *ptr = 97, nachdem ich , dass Ihr Übersetzer ein begegnet ., sollte es dann Aufruf

write(1, ptr, 1)

oder eine äquivalente Druckanweisung zum Drucken des aktuell angezeigten Bytes, das den Wert 97 hat und der Buchstabe awird dann auf dem gedruckt std_output.

Rapdean
quelle
1

Ich denke, Sie fragen sich, woher Brainfuck weiß, was mit dem gesamten Code zu tun ist. Es gibt einen Parser, der in einer höheren Sprache wie Python geschrieben ist, um zu interpretieren, was ein Punkt oder was ein Additionszeichen im Code bedeutet.

Der Parser liest Ihren Code also Zeile für Zeile und sagt, ok, es gibt ein> Symbol, also muss ich den Speicherort vorrücken. Der Code lautet einfach, wenn (Inhalt in diesem Speicherort) ==>, memlocation = + memlocation, was ist geschrieben in einer höheren Sprache, ähnlich wenn (Inhalt im Speicherort) == ".", dann drucken (Inhalt des Speicherorts).

Hoffe das klärt es auf. tc

Rahul
quelle