Come funziona effettivamente Brainfuck Hello World?


118

Qualcuno mi ha inviato questo e ha affermato che è un ciao mondo a Brainfuck (e spero di sì ...)

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

Conosco le basi che funziona spostando un puntatore e incrementando e diminuendo le cose ...

Eppure voglio ancora sapere, come funziona effettivamente? Come fa a stampare qualcosa sullo schermo in primo luogo? Come codifica il testo? Non capisco affatto ...


31
Deve essere piuttosto difficile mantenere un'applicazione scritta in questa lingua ..
e2-e4

17
@ ring0: nah, è una lingua di sola scrittura.
LetMeSOThat4U

qual è l'uso pratico?
Yash Kumar Verma

10
@YashVerma non ne ha bisogno ..
Insane

49
@YashVerma È chiaramente specificato nel nome della lingua.
Mateen Ulhaq

Risposte:


255

1. Nozioni di base

Per capire Brainfuck devi immaginare una serie infinita di celle inizializzate da 0ciascuna.

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

Quando il programma Brainfuck viene avviato, punta a qualsiasi cella.

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

Se sposti il ​​puntatore a destra, >stai spostando il puntatore dalla cella X alla cella X + 1

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

Se aumenti il ​​valore della cella +ottieni:

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

Se aumenti di nuovo il valore della cella, +ottieni:

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

Se diminuisci il valore della cella -ottieni:

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

Se sposti il ​​puntatore a sinistra, <stai spostando il puntatore dalla cella X alla cella X-1

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

2. Ingresso

Per leggere il carattere usi la virgola ,. Quello che fa è: legge il carattere dallo standard input e scrive il suo codice ASCII decimale nella cella effettiva.

Dai un'occhiata alla tabella ASCII . Ad esempio, il codice decimale di !è 33, mentre aè 97.

Bene, immaginiamo che la memoria del tuo programma BF sia simile a:

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

Supponendo che lo standard input stia per a, se usi l' ,operatore virgola , ciò che BF fa è leggere il acodice ASCII decimale 97in memoria:

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

In genere vuoi pensare in questo modo, tuttavia la verità è un po 'più complessa. La verità è che BF non legge un carattere ma un byte (qualunque sia quel byte). Lascia che ti mostri un esempio:

In linux

$ printf ł

stampe:

ł

che è il carattere polacco specifico. Questo carattere non è codificato dalla codifica ASCII. In questo caso è la codifica UTF-8, quindi richiedeva più di un byte nella memoria del computer. Possiamo dimostrarlo facendo un dump esadecimale:

$ printf ł | hd

che mostra:

00000000  c5 82                                             |..|

Gli zeri sono compensati. 82è il primo ec5 è il secondo byte che rappresenta ł(in ordine li leggeremo). |..|è una rappresentazione grafica che non è possibile in questo caso.

Bene, se passi łcome input al tuo programma BF che legge un byte singolo, la memoria del programma sarà simile a:

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

Perché 197? Beh, il 197decimale èc5 esadecimale. Sembra familiare? Ovviamente. È il primo byte di ł!

3. Uscita

Per stampare il carattere si utilizza il punto .Quello che fa è: Supponendo di trattare il valore effettivo della cella come un codice ASCII decimale, stampare il carattere corrispondente sullo standard output.

Bene, immaginiamo che la memoria del tuo programma BF sia simile a:

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

Se ora usi l'operatore punto (.), Ciò che BF fa è stampare:

un'

Perché il acodice decimale in ASCII è 97.

Quindi, ad esempio, un programma BF come questo (97 più 2 punti):

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

Aumenterà il valore della cella che punta fino a 97 e lo stamperà 2 volte.

aa

4. Loop

In BF il ciclo è costituito dall'inizio [e dalla fine del ciclo ]. Puoi pensare che sia come in C / C ++ dove la condizione è il valore effettivo della cella.

Dai un'occhiata al programma BF di seguito:

++[]

++ incrementa due volte il valore effettivo della cella:

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

Ed []è come while(2) {}, quindi è un ciclo infinito.

Diciamo che non vogliamo che questo ciclo sia infinito. Possiamo fare ad esempio:

++[-]

Quindi ogni volta che un ciclo viene ripetuto, il valore effettivo della cella diminuisce. Una volta che il valore effettivo della cella è 0terminato il ciclo:

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

Consideriamo ancora un altro esempio di ciclo finito:

++[>]

Questo esempio mostra che non dobbiamo terminare il ciclo nella cella in cui il ciclo è iniziato:

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

Tuttavia è buona norma finire dove siamo partiti. Perché ? Perché se il ciclo termina un'altra cella è iniziata, non possiamo presumere dove sarà il puntatore della cella. Ad essere onesti, questa pratica rende brainfuck meno brainfuck.


4
Fantastico, ora l'ho capito :)
speeder

25
Quella era una soluzione perfetta per i principianti che cercavano di comprendere questa ideologia del linguaggio. Congratulazioni e ottimo post.
Casey

4
La migliore intro di Brainfuck che abbia mai visto. Onestamente annulli un po 'BF con il tuo post
Boyang

3
Immagino che se hai bisogno di un progetto per il tuo tempo libero, puoi sempre aggiungere il supporto Unicode a Brainfuck.
Álvaro González

3
Dopo il tuo post, BF è! BF più!
grazie a un

52

Wikipedia ha una versione commentata del codice.

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

Per rispondere alle tue domande, il , e. caratteri vengono utilizzati per l'I / O. Il testo è ASCII.

L' articolo di Wikipedia prosegue anche in modo più approfondito.

La prima riga si inizializza a[0] = 10semplicemente incrementando dieci volte da 0. Il ciclo dalla riga 2 imposta effettivamente i valori iniziali per l'array: a[1] = 70(vicino a 72, il codice ASCII per il carattere 'H'), a[2] = 100(vicino a 101 o 'e' ), a[3] = 30(vicino a 32, il codice per lo spazio) e a[4] = 10(nuova riga). Il ciclo funziona aggiungendo 7, 10, 3, e 1, alle cellule a[1], a[2], a[3]e a[4]rispettivamente ogni iterazione - 10 aggiunte per ogni cella in totale (dando a[1]=70ecc). Dopo che il ciclo è finito, a[0]è zero. >++.quindi sposta il puntatore sua[1] , che contiene 70, aggiunge due (producendo 72, che è il codice di caratteri ASCII di una H maiuscola) e lo restituisce.

La riga successiva sposta il puntatore della matrice su a[2] e ne aggiunge uno, producendo 101, una 'e' minuscola, che viene quindi emessa.

Poiché la "l" è la settima lettera dopo la "e", all'output "ll" vengono aggiunte altre sette ( +++++++) a[2]e il risultato viene emesso due volte.

'o' è la terza lettera dopo la 'l', quindi a[2]viene incrementata altre tre volte e restituisce il risultato.

Il resto del programma prosegue allo stesso modo. Per lo spazio e le lettere maiuscole, vengono selezionate celle di matrice diverse e incrementate o decrementate secondo necessità.


Ma PERCHÉ stampa? o come? I commenti mi stanno spiegando l'intento della linea, ora cosa fa.
speeder

8
Stampa perché il compilatore lo sa ,e .sono usati per I / O, proprio come le stampe in C usando putchar. È un dettaglio di implementazione gestito dal compilatore.
ken

1
E anche perché imposta le celle richieste sui valori interi per i caratteri ASCII in "Hello World"
slugonamission

Mi aspettavo una spiegazione più approfondita ... ma: /
speeder

1
@speeder - Ho aggiunto la spiegazione approfondita del codice da Wikipedia alla risposta. Puoi vedere l'articolo collegato per ulteriori informazioni.
ken

9

Per rispondere alla domanda su come sa cosa stampare, ho aggiunto il calcolo dei valori ASCII a destra del codice dove avviene la stampa:

> 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 come il suo nome. Utilizza solo 8 caratteri > [ . ] , - +che lo rendono il linguaggio di programmazione più veloce da imparare ma più difficile da implementare e capire. ... e ti fa finalmente finire con il fottuto cervello.

Memorizza i valori in array: [72] [101] [108] [111]

let, inizialmente puntatore che punta alla cella 1 dell'array:

  1. > sposta il puntatore a destra di 1

  2. < sposta il puntatore a sinistra di 1

  3. + incrementa il valore della cella di 1

  4. - incrementa il valore dell'elemento di 1

  5. . stampa il valore della cella corrente.

  6. , prendere l'input alla cella corrente.

  7. [ ] loop, +++ [-] contatore di 3 conteggi bcz ha 3 ′ + 'prima di esso e - decrementa la variabile di conteggio di 1 valore.

i valori memorizzati nelle celle sono valori ASCII:

quindi facendo riferimento all'array sopra: [72] [101] [108] [108] [111] se corrispondi ai valori ascii scoprirai che è Hello writer

Congratulazioni! hai imparato la sintassi di BF

——- Qualcosa di più ———

realizziamo il nostro primo programma cioè Hello World , dopodiché potrai scrivere il tuo nome in questa lingua.

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

rompendo in pezzi:

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

Crea un array di 4 celle (numero di>) e imposta un contatore di 10 qualcosa come: —-psuedo code—-

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

poiché il valore del contatore è memorizzato nella cella 0 e> si sposta nella cella 1 aggiorna il suo valore di + 7> si sposta nella cella 2 incrementa 10 al valore precedente e così via….

<<< torna alla cella 0 e diminuisce il suo valore di 1

quindi dopo il completamento del ciclo abbiamo array: [70,100,30,10]

>++. 

si sposta al primo elemento e incrementa il suo valore di 2 (due "+"), quindi stampa il carattere (".") con quel valore ASCII. cioè per esempio in python: chr (70 + 2) # stampa 'H'

>+.

passa alla seconda cella incrementa 1 al suo valore 100 + 1 e stampa ('.') il suo valore cioè chr (101) chr (101) # stampa 'e' ora non c'è> o <nel pezzo successivo quindi prende il valore attuale dell'ultimo elemento e incrementalo solo

+++++ ++..

latest element = 101 quindi, 101 + 7 e lo stampa due volte (dato che ci sono due '..') chr (108) #prints l due volte può essere usato come

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

——— Dove viene utilizzato? ---

È solo un linguaggio scherzoso creato per sfidare i programmatori e non viene utilizzato praticamente da nessuna parte.


4

Tutte le risposte sono esaustive, ma mancano di un piccolo dettaglio: la stampa. Nel costruire il tuo traduttore brainfuck, consideri anche il personaggio ., questo è in realtà l'aspetto di una dichiarazione di stampa in brainfuck. Quindi quello che il tuo traduttore brainfuck dovrebbe fare è che ogni volta che incontra un .carattere stampa il byte attualmente puntato.

Esempio:

supponi di avere -> char *ptr = [0] [0] [0] [97] [0]... se questa è un'affermazione brainfuck: il >>>.tuo puntatore dovrebbe essere spostato di 3 spazi a destra, atterrando su :, [97]quindi ora *ptr = 97, dopo averlo fatto il tuo traduttore incontra a ., dovrebbe chiamare

write(1, ptr, 1)

o qualsiasi istruzione di stampa equivalente per stampare il byte attualmente puntato, che ha il valore 97 e la lettera averrà quindi stampata sul std_output.


1

Penso che quello che stai chiedendo sia come fa Brainfuck a sapere cosa fare con tutto il codice. C'è un parser scritto in un linguaggio di livello superiore come Python per interpretare il significato di un punto o il significato di un segno di addizione nel codice.

Quindi il parser leggerà il tuo codice riga per riga e dirà ok c'è un simbolo> quindi devo avanzare nella posizione di memoria, il codice è semplicemente, se (contenuto in quella posizione di memoria) ==>, memlocation = + memlocation che è scritto in un linguaggio di livello superiore, in modo simile se (contenuto nella posizione di memoria) == ".", quindi stampa (contenuto della posizione di memoria).

Spero che questo chiarisca tutto. tc

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.