web-dev-qa-db-fra.com

Comment fonctionne le Brainfuck Hello World?

Quelqu'un m'a envoyé ceci et a prétendu que c'était un monde bonjour à Brainfuck (et j'espère bien ...)

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

Je connais les bases pour que cela fonctionne en déplaçant un pointeur et en incrémentant et décrémentant des choses ...

Pourtant, je veux toujours savoir comment cela fonctionne réellement. Comment est-ce qu'il imprime quoi que ce soit sur l'écran en premier lieu? Comment encode-t-il le texte? Je ne comprends pas du tout...

110
speeder

1. Bases

Pour comprendre Brainfuck, vous devez imaginer un tableau infini de cellules initialisées par 0 Chacune.

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

Lorsque le programme brainfuck commence, il pointe vers n'importe quelle cellule.

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

Si vous déplacez le pointeur à droite >, Vous déplacez le pointeur de la cellule X à la cellule X + 1

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

Si vous augmentez la valeur de la cellule +, Vous obtenez:

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

Si vous augmentez à nouveau la valeur de la cellule +, Vous obtenez:

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

Si vous diminuez la valeur de la cellule -, Vous obtenez:

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

Si vous déplacez le pointeur à gauche <, Vous déplacez le pointeur de la cellule X à la cellule X-1.

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

2. entrée

Pour lire le caractère, utilisez la virgule ,. Ce qu'il fait est: Lit le caractère de l'entrée standard et écrit son code décimal ASCII dans la cellule réelle.

Jetez un oeil à table ASCII . Par exemple, le code décimal de ! Est 33, Tandis que a est 97.

Eh bien, imaginons que la mémoire de votre programme BF ressemble à:

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

En supposant que l'entrée standard représente a, si vous utilisez l'opérateur virgule ,, Ce que fait BF est lu a décimal ASCII code 97 à la mémoire:

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

Vous voulez généralement penser ainsi, mais la vérité est un peu plus complexe. La vérité est que BF ne lit pas un caractère, mais un octet (quel que soit cet octet). Laissez-moi vous montrer un exemple:

Sous linux

$ printf ł

impressions:

ł

qui est le caractère polonais spécifique. Ce caractère n'est pas codé par le codage ASCII. Dans ce cas, il s’agit du codage UTF-8, qui prenait alors plus d’un octet dans la mémoire de l’ordinateur. Nous pouvons le prouver en effectuant un vidage hexadécimal:

$ printf ł | hd

qui montre:

00000000  c5 82                                             |..|

Les zéros sont compensés. 82 Est le premier et c5 Est le deuxième octet représentant ł (Dans l'ordre, nous allons les lire). |..| Est une représentation graphique qui n'est pas possible dans ce cas.

Eh bien, si vous transmettez ł Comme entrée à votre programme BF qui lit un octet, la mémoire programme ressemblera à ceci:

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

Pourquoi 197? Eh bien 197 Le nombre décimal est c5 Hexadécimal. Semble familier? Bien sûr. C'est le premier octet de ł!

3. sortie

Pour imprimer le caractère que vous utilisez, vous utilisez dot .. Il agit comme suit: En supposant que nous traitions la valeur réelle de la cellule comme un code décimal ASCII, imprimez le caractère correspondant sur la sortie standard.

Eh bien, imaginons que la mémoire de votre programme BF ressemble à:

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

Si vous utilisez maintenant l’opérateur point (.), Ce que fait BF est d’imprimer:

une

Parce que a le code décimal dans ASCII est 97.

Ainsi, par exemple, un programme BF comme celui-ci (97 plus 2 points):

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

Augmentera la valeur de la cellule pointée jusqu'à 97 et l'imprimera 2 fois.

aa

4. Boucles

Dans la boucle BF, la boucle commence par la boucle [ Et la fin de la boucle ]. Vous pouvez penser que c'est comme en C/C++ où la condition est la valeur réelle de la cellule.

Jetez un coup d'œil au programme BF ci-dessous:

++[]

++ Incrémente deux fois la valeur réelle de la cellule:

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

Et [] Est comme while(2) {}, donc c'est une boucle infinie.

Disons que nous ne voulons pas que cette boucle soit infinie. Nous pouvons faire par exemple:

++[-]

Ainsi, chaque boucle en boucle réduit la valeur réelle de la cellule. Une fois que la valeur réelle de la cellule est 0, La boucle se termine:

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

Considérons encore un autre exemple de boucle finie:

++[>]

Cet exemple montre, nous ne devons pas terminer la boucle à la cellule qui a démarré la boucle:

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

Cependant, il est bon de terminer là où nous avons commencé. Pourquoi ? Parce que si la boucle termine une autre cellule, elle a commencé, nous ne pouvons pas supposer où le pointeur de la cellule sera. Pour être honnête, cette pratique rend le brainfuck moins cérébral.

233
Scony

Wikipedia a une version commentée du code.

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

Pour répondre à vos questions, le , et . caractères sont utilisés pour les E/S. Le texte est ASCII.

L’article Wikipedia est également plus approfondi.

La première ligne initialise a[0] = 10 en incrémentant simplement dix fois la valeur 0. La boucle de la ligne 2 définit efficacement les valeurs initiales du tableau: a[1] = 70 (près de 72, le ASCII du code 'H'), a[2] = 100 (près de 101 ou 'e'), a[3] = 30 (près de 32, le code pour l'espace) et a[4] = 10 (nouvelle ligne). La boucle fonctionne en ajoutant 7, 10, 3 et 1 aux cellules a[1], a[2], a[3] et a[4] respectivement à chaque boucle - 10 additions pour chaque cellule au total (donnant a[1]=70 etc.). Une fois la boucle terminée, a[0] est zéro. >++. déplace ensuite le pointeur sur a[1], qui en détient 70, en ajoute deux (produisant 72, qui est le code de caractère ASCII d'un H majuscule), et le sort.

La ligne suivante déplace le pointeur du tableau sur a[2] et y ajoute un, produisant 101, un 'e' minuscule, qui est ensuite émis.

Comme 'l' est la septième lettre après 'e', ​​sept autres sont ajoutés pour produire 'll' (+++++++) à a[2] et le résultat est imprimé deux fois.

'o' est la troisième lettre après 'l', donc a[2] est incrémenté trois fois de plus et affiche le résultat.

Le reste du programme se poursuit de la même manière. Pour les lettres majuscules et majuscules, différentes cellules du tableau sont sélectionnées et incrémentées ou décrémentées selon les besoins.

50
ken

Pour répondre à la question de savoir comment il sait quoi imprimer, j'ai ajouté le calcul des valeurs ASCII à la droite du code où se déroule l'impression:

> 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)
8
Rehana Mahfuz

Brainfuck identique à son nom. Il utilise seulement 8 caractères > [ . ] , - + qui en fait le le plus rapide langage de programmation pour apprendre mais le plus difficile à mettre en œuvre et comprendre.… .et rend vous vous retrouvez finalement avec votre cerveau.

Il stocke les valeurs dans un tableau: [72] [101] [108] [111]

laisser, initialement pointeur pointant vers la cellule 1 du tableau:

  1. > déplace le pointeur vers la droite de 1

  2. < déplace le pointeur vers la gauche de 1

  3. + incrémente la valeur de la cellule de 1

  4. - incrémente la valeur d'élément de 1

  5. . imprime la valeur de la cellule en cours.

  6. , prend une entrée dans la cellule en cours.

  7. [ ] boucle, +++ [-] compteur de 3 comptes bcz, il a 3′ + ’avant et - décrémente variable de 1 par valeur.

les valeurs stockées dans les cellules sont des valeurs ascii:

pour faire référence au tableau ci-dessus: [72] [101] [108] [108] [111] si vous correspondez aux valeurs ascii, vous constaterez qu’il s’agit de Hello writetern

Félicitations! vous avez appris la syntaxe de BF

——-Quelque chose de plus ———

faisons notre premier programme i.e Hello World, après quoi vous pourrez écrire votre nom dans cette langue.

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

rupture en morceaux:

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

Crée un tableau de 4 cellules (nombre de>) et définit un compteur de 10, par exemple:

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

car la valeur du compteur est stockée dans la cellule 0 et que> déplacer à la cellule 1 met à jour sa valeur de + 7> déplacer à la cellule 2 incrémente de 10 sa valeur précédente et ainsi de suite….

<<< retourne à la cellule 0 et diminue sa valeur de 1

par conséquent, après achèvement de la boucle, nous avons le tableau: [70,100,30,10]

>++. 

passe au 1er élément et incrémente sa valeur de 2 (deux "+"), puis affiche (".") le caractère avec cette valeur ascii. c'est-à-dire en python: chr (70 + 2) # affiche 'H'

>+.

passe au 2ème incrément de cellule 1 à sa valeur 100 + 1 et affiche ('.') sa valeur, c.-à-d. chr (101) chr (101) #prints 'e' maintenant il n'y a pas de> ou <dans le morceau suivant et prend donc la valeur actuelle du dernier élément et incrément à seulement

+++++ ++..

dernier élément = 101 donc 101 + 7 et l’imprime deux fois (car il y a deux "..") chr (108) #prints l deux fois peut être utilisé comme

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

———Où est-il utilisé? ——-

C'est juste un langage de plaisanterie conçu pour contester les programmeurs et n'est utilisé pratiquement nulle part.

7
TheVishal

Toutes les réponses sont complètes, mais il leur manque un petit détail: l’impression. En construisant votre traducteur brainfuck, vous considérez également le caractère ., c’est en fait ce à quoi ressemble un énoncé d’impression dans brainfuck. Donc, ce que votre traducteur doit faire, c'est quand il rencontre un . caractère il affiche l'octet pointé actuellement.

Exemple:

supposons que vous ayez -> char *ptr = [0] [0] [0] [97] [0]... s'il s'agit d'une déclaration brainfuck: >>>. votre pointeur doit être déplacé de 3 espaces vers l’atterrissage à droite à: [97], alors maintenant *ptr = 97, après que votre traducteur rencontre un ., il devrait alors appeler

write(1, ptr, 1)

ou n’importe quelle instruction d’impression équivalente pour imprimer l’octet actuellement pointé, qui a la valeur 97 et la lettre a sera alors imprimée sur le std_output.

4
rapdean

Je pense que ce que vous demandez, c'est comment Brainfuck sait quoi faire avec tout le code. Un analyseur syntaxique est écrit dans un langage de niveau supérieur tel que Python) pour interpréter la signification d'un point ou la signification d'un signe d'addition dans le code.

Donc, l'analyseur lit votre code ligne par ligne et dit ok il y a un symbole>, donc je dois avancer l'emplacement de la mémoire, le code est simplement si (le contenu de cet emplacement de mémoire) ==>, memlocation = + memlocation qui est écrit dans un langage de niveau supérieur, de même si (contenu dans l’emplacement de la mémoire) == ".", puis imprimer (contenu de l’emplacement de la mémoire).

J'espère que cela clarifie les choses. tc

1
Rahul