Como o Brainfuck Hello World realmente funciona?

118

Alguém me enviou isso e afirmou que é um olá, mundo em Brainfuck (e espero que sim ...)

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

Eu sei o básico que funciona movendo um ponteiro e aumentando e diminuindo coisas ...

Mesmo assim, quero saber como isso realmente funciona? Como ele imprime qualquer coisa na tela em primeiro lugar? Como ele codifica o texto? Eu não entendo nada...

speeder
fonte
31
Deve ser muito difícil manter um aplicativo escrito neste idioma.
e2-e4
17
@ ring0: nah, essa é uma linguagem somente para escrita.
LetMeSOThat4U
qual é o uso prático?
Yash Kumar Verma de
10
@YashVerma não precisa de um ..
Insano
49
@YashVerma É claramente especificado no nome do idioma.
Mateen Ulhaq

Respostas:

255

1. Básico

Para entender o Brainfuck, você deve imaginar um conjunto infinito de células inicializadas por 0cada 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 ais 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 acódigo ASCII decimal 97para 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 c5segundo 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, 197decimal é c5hexadecimal. 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 acó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 0terminar 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.

Varanda
fonte
4
Legal, agora entendi :)
speeder
25
Essa foi uma solução perfeita para o novato que tentava compreender essa ideologia da linguagem. Parabéns e ótima postagem.
Casey
4
Melhor introdução do Brainfuck que já vi. Honestamente, você desfez um pouco o BF com sua postagem
Boyang
3
Eu acho que se você precisa de um projeto para seu tempo livre, você sempre pode adicionar suporte Unicode para Brainfuck.
Álvaro González
3
Depois da sua postagem, BF é! BF anymore!
thanos.a
52

A Wikipedia tem uma versão comentada do código.

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

Para responder às suas perguntas, o ,e. caracteres são usados ​​para E / S. O texto é ASCII.

O artigo da Wikipedia também continua com um pouco mais de profundidade.

A primeira linha é inicializada a[0] = 10simplesmente incrementando dez vezes a partir de 0. O loop da linha 2 define efetivamente os valores iniciais para a matriz: a[1] = 70(perto de 72, o código ASCII para o caractere 'H'), a[2] = 100(perto de 101 ou 'e' ), a[3] = 30(perto de 32, o código para espaço) e a[4] = 10(nova linha). O loop funciona adicionando 7, 10, 3 e 1 às células a[1], e a[2], respectivamente, a cada vez através do loop - 10 adições para cada célula no total (dando, etc.). Após o término do loop, é zero. em seguida, move o ponteiro para , que contém 70, adiciona dois a ele (produzindo 72, que é o código de caractere ASCII de um H maiúsculo) e o produz.a[3]a[4]a[1]=70a[0]>++.a[1]

A próxima linha move o ponteiro do array para a[2]e adiciona um a ele, produzindo 101, um 'e' minúsculo, que é então gerado.

Como 'l' é a sétima letra depois de 'e', ​​à saída de 'll' outras sete são adicionadas ( +++++++) a a[2]e o resultado é gerado duas vezes.

'o' é a terceira letra depois de 'l', então a[2]é incrementado mais três vezes e gera o resultado.

O resto do programa continua da mesma maneira. Para o espaço e letras maiúsculas, diferentes células da matriz são selecionadas e aumentadas ou diminuídas conforme necessário.

ken
fonte
Mas POR QUE é impresso? ou como? Os comentários estão me explicando a intenção da linha, agora o que ela faz.
speeder
8
Ele imprime porque o compilador sabe disso ,e .é usado para E / S, da mesma forma que o C imprime usando putchar. É um detalhe de implementação tratado pelo compilador.
ken
1
E também porque está configurando as células necessárias para valores inteiros para os caracteres ASCII em "Hello World"
slugonamission
Eu esperava uma explicação mais aprofundada ... mas: /
speeder
1
@speeder - adicionei a explicação detalhada do código da Wikipedia à resposta. Você pode ver o artigo vinculado para mais informações.
ken
9

Para responder à pergunta de como ele sabe o que imprimir, adicionei o cálculo dos valores ASCII à direita do código onde ocorre a impressão:

> 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
fonte
9

Brainfuck tem o mesmo nome. Ele usa apenas 8 caracteres, o > [ . ] , - +que o torna a linguagem de programação mais rápida de aprender, mas mais difícil de implementar e entender. … .E faz você finalmente acabar fodendo seu cérebro.

Ele armazena valores na matriz: [72] [101] [108] [111]

deixe, inicialmente o ponteiro apontando para a célula 1 da matriz:

  1. > mova o ponteiro para a direita em 1

  2. < mova o ponteiro para a esquerda em 1

  3. + incrementa o valor da célula em 1

  4. - incrementa o valor do elemento em 1

  5. . imprime o valor da célula atual.

  6. , leva a entrada para a célula atual.

  7. [ ] loop, +++ [-] contador de 3 contagens bcz tem 3 ′ + 'antes dele, e - diminui a variável de contagem em 1 valor.

os valores armazenados nas células são valores ascii:

portanto, referindo-se ao array acima: [72] [101] [108] [108] [111] se você combinar os valores ASCII, você descobrirá que é Hello Writetern

Parabéns! você aprendeu a sintaxe do BF

——- Algo mais ———

vamos fazer nosso primeiro programa, isto é, Hello World , após o qual você poderá escrever seu nome nesta linguagem.

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

quebrando em pedaços:

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

Faz uma matriz de 4 células (número de>) e define um contador de 10 algo como: —- código psuedo—-

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

porque o valor do contador é armazenado na célula 0 e> move para a célula 1 atualiza seu valor em + 7> move para a célula 2 incrementa 10 para seu valor anterior e assim por diante….

<<< retorna para a célula 0 e diminui seu valor em 1

portanto, após a conclusão do loop, temos o array: [70,100,30,10]

>++. 

move para o primeiro elemento e incrementa seu valor em 2 (dois '+') e então imprime ('.') caractere com esse valor ascii. por exemplo, em python: chr (70 + 2) # imprime 'H'

>+.

move-se para o segundo incremento de célula 1 para seu valor 100 + 1 e imprime ('.') seu valor, ou seja, chr (101) chr (101) #impressa 'e' agora não há> ou <na próxima peça, então assume o valor presente do último elemento e incremento apenas para ele

+++++ ++..

elemento mais recente = 101, portanto, 101 + 7 e imprime-o duas vezes (pois há dois '..') chr (108) #prints l duas vezes pode ser usado como

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

——— Onde é usado? ——-

É apenas uma linguagem de piada feita para desafiar os programadores e não é usada em praticamente qualquer lugar.

DARK_C0D3R
fonte
4

Todas as respostas são completas, mas falta um pequeno detalhe: impressão. Ao construir o seu tradutor de brainfuck, você também considera o personagem ., isto é realmente o que uma declaração impressa se parece no brainfuck. Portanto, o que seu tradutor cerebral deve fazer é, sempre que encontrar um .caractere, ele imprimirá o byte atualmente apontado.

Exemplo:

suponha que você tenha -> char *ptr = [0] [0] [0] [97] [0]... se esta é uma afirmação de brainfuck: >>>.seu ponteiro deve ser movido 3 espaços para a direita em:, [97]então agora *ptr = 97, depois de fazer isso, seu tradutor encontra um ., ele deve então chamar

write(1, ptr, 1)

ou qualquer instrução de impressão equivalente para imprimir o byte apontado atualmente, que tem o valor 97 e a letra aserá então impressa no std_output.

Rapdean
fonte
1

Acho que você está perguntando como o Brainfuck sabe o que fazer com todo o código. Há um analisador escrito em uma linguagem de nível superior, como Python, para interpretar o que significa um ponto ou o que um sinal de adição significa no código.

Assim, o analisador lerá seu código linha por linha e dirá ok, há um símbolo>, então eu tenho que avançar a localização da memória, o código é simplesmente, if (conteúdo nessa localização da memória) ==>, memlocation = + memlocation que é escrito em uma linguagem de nível superior, da mesma forma se (conteúdo na localização da memória) == ".", então imprima (conteúdo da localização da memória).

Espero que esteja entendido. tc

Rahul
fonte