1. Básico
Para entender o Brainfuck, você deve imaginar um conjunto infinito de células inicializadas por 0
cada um.
...[0][0][0][0][0]...
Quando o programa Brainfuck é iniciado, ele aponta para qualquer célula.
...[0][0][*0*][0][0]...
Se você mover o ponteiro para a direita, >
você está movendo o ponteiro da célula X para a célula X + 1
...[0][0][0][*0*][0]...
Se você aumentar o valor da célula, +
obterá:
...[0][0][0][*1*][0]...
Se você aumentar o valor da célula novamente, +
obterá:
...[0][0][0][*2*][0]...
Se você diminuir o valor da célula, -
obterá:
...[0][0][0][*1*][0]...
Se você mover o ponteiro para a esquerda, <
você está movendo o ponteiro da célula X para a célula X-1
...[0][0][*0*][1][0]...
2. Entrada
Para ler caracteres, você usa vírgula ,
. O que ele faz é: Ler o caractere da entrada padrão e gravar seu código ASCII decimal na célula real.
Dê uma olhada na tabela ASCII . Por exemplo, código decimal de !
is 33
, while a
is 97
.
Bem, vamos imaginar que a memória do seu programa BF seja semelhante a:
...[0][0][*0*][0][0]...
Assumindo que a entrada padrão representa a
, se você usar o ,
operador vírgula , o que BF faz é ler o a
código ASCII decimal 97
para a memória:
...[0][0][*97*][0][0]...
Você geralmente quer pensar dessa maneira, mas a verdade é um pouco mais complexa. A verdade é que o BF não lê um caractere, mas um byte (seja ele qual for). Deixe-me mostrar um exemplo:
Em linux
$ printf ł
estampas:
ł
que é um caráter polonês específico. Este caractere não é codificado pela codificação ASCII. Nesse caso, é a codificação UTF-8, então costumava ocupar mais de um byte na memória do computador. Podemos provar isso fazendo um despejo hexadecimal:
$ printf ł | hd
que mostra:
00000000 c5 82 |..|
Zeros são compensados. 82
é o primeiro e o c5
segundo byte representando ł
(para que possamos lê-los).|..|
é a representação gráfica que não é possível neste caso.
Bem, se você passar ł
como entrada para o seu programa BF que lê byte único, a memória do programa será semelhante a:
...[0][0][*197*][0][0]...
Porque 197
? Bem, 197
decimal é c5
hexadecimal. Parece familiar? Claro. É o primeiro byte de ł
!
3. Saída
Para imprimir o caractere, você usa o ponto. .
O que ele faz é: Assumindo que tratamos o valor real da célula como código ASCII decimal, imprime o caractere correspondente na saída padrão.
Bem, vamos imaginar que a memória do seu programa BF seja semelhante a:
...[0][0][*97*][0][0]...
Se você usar o operador ponto (.) Agora, o que BF faz é imprimir:
uma
Porque o a
código decimal em ASCII é 97
.
Então, por exemplo, programa BF como este (97 mais 2 pontos):
+++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++++++++++++++++++++++
Aumentará o valor da célula que aponta para até 97 e imprime 2 vezes.
aa
4. Loops
Em BF, o loop consiste no início [
e no final do loop ]
. Você pode pensar que é como em C / C ++, onde a condição é o valor real da célula.
Dê uma olhada no programa BF abaixo:
++[]
++
incrementa o valor real da célula duas vezes:
...[0][0][*2*][0][0]...
E []
é assim while(2) {}
, então é um loop infinito.
Digamos que não queremos que esse loop seja infinito. Podemos fazer por exemplo:
++[-]
Portanto, cada vez que um loop faz um loop, ele diminui o valor real da célula. Assim que o valor real da célula 0
terminar o loop:
...[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)
Vamos considerar outro exemplo de loop finito:
++[>]
Este exemplo mostra que não devemos terminar o loop na célula em que o loop começou:
...[0][0][*2*][0][0]... loop starts
...[0][0][2][*0*][0]... after first iteration (loop ends)
No entanto, é uma boa prática terminar onde começamos. Por quê ? Porque se o loop termina em outra célula, ele começou, não podemos assumir onde o ponteiro da célula estará. Para ser honesto, essa prática torna a foda cerebral menos foda.