148 votes

Comment fonctionne réellement le Hello World de Brainfuck ?

Quelqu'un m'a envoyé ceci en affirmant qu'il s'agit d'un hello world en Brainfuck (et je l'espère...).

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

Je connais les bases, à savoir que ça fonctionne en déplaçant un pointeur et en incrémentant et décrémentant des trucs...

Pourtant, je veux toujours savoir comment cela fonctionne réellement. Comment imprime-t-il quelque chose à l'écran ? Comment le texte est-il codé ? Je ne comprends pas du tout...

303voto

Scony Points 744

1. Principes de base

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

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

Quand le programme brainfuck démarre, il pointe vers n'importe quelle cellule.

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

Si vous déplacez le pointeur vers la 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 vers la 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, vous utilisez la virgule , . Ce qu'il fait est : Lit le caractère depuis l'entrée standard et écrit son code ASCII décimal dans la cellule actuelle.

Jetez un coup d'œil à Table ASCII . Par exemple, le code décimal de ! es 33 alors que a es 97 .

Imaginons que la mémoire de votre programme BF ressemble à ça :

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

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

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

C'est ce que vous voulez généralement penser, 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 :

Dans linux

$ printf ł

des empreintes :

ł

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

$ printf ł | hd

qui montre :

00000000  c5 82                                             |..|

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

Eh bien, si vous passez ł comme entrée de votre programme BF qui lit un seul octet, la mémoire du programme ressemblera à ceci :

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

Pourquoi 197 ? Eh bien 197 décimal est c5 hexadécimal. Cela vous semble familier ? Bien sûr. C'est le premier octet de ł !

3. Sortie

Pour imprimer un caractère, vous utilisez le point . Ce qu'il fait : En supposant que nous traitions la valeur réelle de la cellule comme un code ASCII décimal, nous imprimons le caractère correspondant sur la sortie standard.

Imaginons que la mémoire de votre programme BF ressemble à ça :

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

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

a

Parce que a Le code décimal en ASCII est 97 .

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

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

Augmentera la valeur de la cellule vers laquelle il pointe jusqu'à 97 et l'imprimera 2 fois.

aa

4. Boucles

Dans la boucle BF, il y a une boucle de début [ et fin de la boucle ] . Vous pouvez penser que c'est comme while 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 [] c'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 fois qu'une boucle se déroule, elle décrémente 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 qu'il n'est pas nécessaire de terminer la boucle sur la cellule où elle a commencé :

...[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 se termine dans une autre cellule qu'elle a commencée, nous ne pouvons pas supposer où sera le pointeur de cellule. Pour être honnête, cette pratique rend le brainfuck moins brainfuck.

58voto

ken Points 6183

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 , y . sont utilisés pour les E/S. Le texte est en ASCII.

Le site Wikipedia L'article est également plus approfondi.

La première ligne initialise a[0] = 10 en incrémentant simplement dix fois de 0. La boucle de la ligne 2 définit effectivement les valeurs initiales du tableau le tableau : a[1] = 70 (proche de 72, le code ASCII pour le caractère 'H'), a[2] = 100 (proche de 101 ou 'e'), a[3] = 30 (proche 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] y a[4] respectivement chaque chaque fois que la boucle est parcourue - 10 additions pour chaque cellule au total (donnant a[1]=70 etc.). Une fois la boucle terminée, a[0] est égal à zéro. >++. puis déplace le pointeur vers a[1] qui peut contenir 70 personnes, en ajoute deux (ce qui donne 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 vers a[2] et lui en ajoute un, ce qui donne 101, un "e" minuscule, qui est ensuite édité.

Comme il se doit est la septième lettre après le "e", pour produire "ll", il faut en ajouter sept autres. ajoutées ( +++++++ ) à a[2] et le résultat est édité deux fois.

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

Le reste du programme se déroule de la même manière. Pour les espaces et les majuscules, on choisit des cellules de tableau différentes et incrémentées ou décrémentées selon les besoins.

12voto

TheVishal Points 190

Brainfuck comme son nom. Il n'utilise que 8 caractères > [ . ] , - + ce qui en fait le le plus rapide langage de programmation pour apprendre pero le plus dur à mettre en œuvre et comprendre. .and fait que tu finis par t'envoyer en l'air le cerveau.

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

let, initialement pointeur pointant sur 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 de l'élément par 1

  5. . imprime la valeur de la cellule courante.

  6. , prendre l'entrée à la cellule actuelle.

  7. [ ] boucle, +++[ -] compteur de 3 comptes car il a 3 +' avant lui, et - décrémente la variable de comptage de 1 valeur.

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

donc en se référant au tableau ci-dessus : [72 ][101 ][108 ][108][111 ] si vous faites correspondre les valeurs ascii, vous trouverez que c'est Bonjour écrit sur

Félicitations ! vous avez appris la syntaxe de BF

--- Quelque chose de plus ---

faisons notre premier programme, c'est-à-dire Bonjour le monde après quoi tu pourras écrire ton nom dans cette langue.

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

en se brisant en morceaux :

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

Crée un tableau de 4 cellules (nombre de >) et met un compteur de 10, quelque chose comme : --psuedo code--

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

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

<<< retourne à la cellule 0 et décrémente sa valeur de 1

donc après la fin de la boucle nous avons le tableau : [70,100,30,10]

>++. 

se déplace vers le 1er élément et incrémente sa valeur de 2 (deux '+') puis imprime ('.') le caractère avec cette valeur ascii. par exemple en python : chr(70+2) # imprime 'H'.

>+.

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

+++++ ++..

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

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

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

Ce n'est qu'un langage de plaisanterie destiné à défier les programmeurs et il n'est pratiquement utilisé nulle part.

9voto

Rehana Mahfuz Points 91

Pour répondre à la question de savoir comment il sait ce qu'il doit imprimer, j'ai ajouté le calcul des valeurs ASCII à droite du code où l'impression a lieu :

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

5voto

zeezor Points 51

Toutes les réponses sont complètes, mais il leur manque un petit détail : l'impression. En construisant votre traducteur à la noix, vous tenez également compte du caractère . c'est en fait ce à quoi ressemble une déclaration d'impression dans brainfuck. Donc, ce que votre traducteur brainfuck devrait faire, c'est que chaque fois qu'il rencontre une instruction . il imprime l'octet actuellement pointé.

Exemple :

Supposons que vous ayez char *ptr = [0] [0] [0] [97] [0] ... s'il s'agit d'une déclaration d'encéphalogramme : >>>. votre pointeur devrait être déplacé de 3 espaces vers la droite pour atterrir à : [97] alors maintenant *ptr = 97 après avoir fait cela, votre traducteur rencontre une . il devrait ensuite appeler

write(1, ptr, 1)

ou toute autre instruction d'impression équivalente pour imprimer l'octet actuellement pointé, qui a pour valeur 97 et la lettre a sera alors imprimé sur le std_output .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X