Как на самом деле работает Brainfuck Hello World?

кто-то отправил это мне и утверждал, что это Привет мир в Brainfuck (и я надеюсь, что так...)

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

Я знаю основы, что он работает, перемещая указатель и приращение и уменьшение материала...

но я все еще хочу знать, как это действительно работает? Как он вообще печатает что-либо на экране? Как он кодирует текст? Я ничего не понимаю...

6 ответов


1. Основы

чтобы понять Brainfuck, вы должны представить себе бесконечный массив ячеек, инициализированных 0 каждый.

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

когда программа brainfuck запускается, она указывает на любую ячейку.

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

если вы переместите указатель вправо > вы перемещаете указатель из ячейки X в ячейку X+1

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

если вы увеличиваете значение ячейки + вы получаете:

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

если вы снова увеличите значение ячейки + вы получить:

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

если вы уменьшаете значение ячейки - вы получаете:

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

если вы переместите указатель влево < вы перемещаете указатель из ячейки X в ячейку X-1

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

2. Ввод

для чтения символа вы используете запятую ,. Что он делает:прочитайте символ от стандартного входного сигнала и напишите свой десятичный код ASCII к фактической ячейке.

посмотри таблица ASCII. Например, десятичный код ! и 33, а a и 97.

Ну, давайте представим, что ваша память программы BF выглядит так:

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

предполагая, что стандартный ввод означает a, если вы используете запятую , оператора, что БФ не читал a десятичный код ASCII 97 память:

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

вы обычно хотите думать так, однако правда немного сложнее. Правда в том, что BF не читает Символ, а байт (что угодно это байт). Позвольте мне показать вам пример:

в linux

$ printf ł

принты:

ł

который является специфическим польским характером. Этот символ не кодируется кодировкой ASCII. В этом случае это кодировка UTF-8, поэтому она занимала более одного байта в памяти компьютера. Мы можем доказать это, сделав шестнадцатеричный дамп:

$ printf ł | hd

, который показывает:

00000000  c5 82                                             |..|

нули смещаются. 82 это первый и c5 второй байт представляя ł (для того, чтобы мы их прочитали). |..| - графическое представление, которое в этом случае невозможно.

Ну, если ты сдашь ł в качестве ввода в вашу программу BF, которая читает один байт, память программы будет выглядеть так:

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

почему 197 ? Ну!--41--> decimal - это c5 шестнадцатеричное. Кажется знакомым ? Конечно. Это первый байт ł !

3. Вывод

для печати символа используется точка . что он делает:предполагая, что мы обрабатываем фактическое значение ячейки как десятичный код ASCII, выведите соответствующий символ на стандартный вывод.

Ну, давайте представим, что ваша память программы BF выглядит так:

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

если вы используете dot (.) оператор теперь, что BF делает, это печать:

a

, потому что a десятичный код в ASCII -97.

так, например, программа BF, как это (97 плюсов 2 точек):

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

увеличит значение ячейки, на которую он указывает, до 97 и распечатает его в 2 раза.

aa

4. Петли

в BF loop состоит из цикла begin [ и конец цикла ]. Вы можете думать, что это как в C / C++, где условие является фактической ячейкой значение.

посмотрите программу BF ниже:

++[]

++ увеличивает фактическое значение ячейки в два раза:

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

и [] как while(2) {}, так что это бесконечный цикл.

предположим, мы не хотим, чтобы этот цикл был бесконечным. Мы можем сделать, например:

++[-]

таким образом, каждый раз, когда цикл петли он уменьшает фактическое значение ячейки. Как только фактическое значение ячейки 0 концы петли:

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

давайте рассмотрим еще другой пример конечного цикла:

++[>]

этот пример показывает, что мы не должны заканчивать цикл в ячейке, на которой начался цикл:

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

однако это хорошая практика, чтобы закончить там, где мы начали. Почему ? Потому что, если цикл заканчивается другой ячейкой, мы не можем предположить, где будет указатель ячейки. Честно говоря, эта практика делает brainfuck менее brainfuck.


Википедия имеет прокомментированную версию кода.

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

чтобы ответить на ваши вопросы,, и . символы используются для ввода/вывода текста в формате ASCII.

на Википедия статье далее более подробно, а также.

первая строка инициализирует a[0] = 10, просто увеличивая в десять раз от 0. Цикл из строки 2 эффективно устанавливает начальные значения для массив: a[1] = 70 (близко к 72, код ASCII для символа 'H'),a[2] = 100 (близко к 101 или 'e'), a[3] = 30 (близко к 32, код для пробела) и a[4] = 10 (перевод строки). Цикл работает путем добавления 7, 10, 3 и 1, в ячейки a[1], a[2], a[3] и a[4] соответственно каждый время цикла - 10 дополнений для каждой ячейки в целом (давая a[1]=70 etc.). После завершения цикла,a[0] равна нулю. >++. затем перемещает указатель на a[1], который держит 70, добавляет к нему два (производство 72, который является ASCII-символьным кодом заглавной буквы H)и выводит его.

следующую строку в ячейке a[2] и прибавляет к нему, производя 101, строчную букву "Е", которая затем выводится.

Как 'л' происходит чтобы быть седьмой буквой после "e", для вывода " ll " еще семь добавлено (+++++++) к a[2] и результат выводится дважды.

' o ' - это третья буква после "л", так a[2] увеличивается еще на три время работы и выведите результат.

остальная часть программы продолжается таким же образом. Для пробелов и заглавных букв выбираются различные ячейки массива и увеличенный или уменьшенный по мере необходимости.


чтобы ответить на вопрос, как он знает, что печатать, я добавил расчет значений ASCII справа от кода, где происходит печать:

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

Brainfuck то же, что и название. Он использует только 8 символов > [ . ] , - + что делает его быстрый язык программирования узнать но трудно выполнить и понимаю. ....и Вы наконец закончить С Е*cking ваш мозг.

он хранит значения в массиве: [72 ][101 ][108 ][111 ]

пусть, первоначально указатель, указывающий на ячейку 1 массив:

  1. > переместить указатель вправо на 1

  2. < переместить указатель влево на 1

  3. + увеличить значение ячейки на 1

  4. - увеличить значение элемента 1

  5. . значение печати текущей ячейки.

  6. , примите входной сигнал к течению клетка.

  7. [ ] loop, + + + [ - ] счетчик из 3 отсчетов bcz имеет 3 ' + ' перед ним и-уменьшает переменную count на 1 значение.

значения, хранящиеся в ячейках, являются значениями ascii:

так ссылаясь на вышеуказанный массив: [72 ][101 ][108 ][108][111 ] если вы соответствуете значениям ascii, вы обнаружите, что это Привет writtern

поздравляю! вы изучили синтаксис BF

---что-то больше - - -

давайте сделаем нашу первую программу i.e Привет, Мир, после чего вы можете написать свое имя на этом языке.

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

разорвать на куски:

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

делает массив из 4 ячеек (количество >) и устанавливает счетчик 10 что-то вроде : --код псевдо--

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

поскольку значение счетчика хранится в ячейке 0 и > перемещается в ячейку 1, обновляет его значение по+7 > переходит в ячейку 2 с шагом 10 к предыдущему значению и так далее....

<<< вернитесь в ячейку 0 и уменьшите ее значение на 1

следовательно, после завершения цикла у нас есть массив: [70,100,30,10]

>++. 

перемещается в 1-й элемент и увеличивает его значение на 2(два'+’), а затем печатает(‘.') символ с этим значением ascii. Я. e например, в python: chr (70+2) # печатает 'H'

>+.

перемещается во 2-ю ячейку с шагом 1 до его значения 100+1 и отпечатки(‘.’ ) его значение i.e chr (101) chr (101) #печатает ' e’ теперь нет > или

+++++ ++..

latest element = 101 следовательно, 101+7 и печатает его дважды(как есть два‘..’) chr (108) #печатает l дважды может использоваться как

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

---где он используется? - - -

это просто шутка язык сделан, чтобы бросить вызов программистам и не используется практически везде.


все ответы тщательны, но им не хватает одной крошечной детали: печать. В языке Brainfuck переводчик, вы также рассмотрите характер ., это на самом деле то, что оператор печати выглядит в brainfuck. Так что ваш переводчик brainfuck должен делать, когда он сталкивается с . символ он печатает текущий указанный байт.

пример:

предположим, у вас есть --> char *ptr = [0] [0] [0] [97] [0]... если это заявление brainfuck: >>>. ваш указатель должен быть перемещен на 3 пробела вправо по адресу:[97], теперь *ptr = 97, после этого ваш переводчик обнаруживает ., затем он должен назвать

write(1, ptr, 1)

или любой эквивалентный оператор печати для печати текущего указанного байта, который имеет значение 97 и буквы a затем будет напечатано на std_output.


Я думаю, что вы спрашиваете, как Brainfuck знает, что делать со всем кодом. Существует синтаксический анализатор, написанный на языке более высокого уровня, таком как Python, чтобы интерпретировать, что означает точка или что означает знак добавления в коде.

таким образом, парсер будет читать ваш код строка за строкой, и сказать ok есть > символ, поэтому я должен заранее расположение памяти, код просто, если (содержимое в этом месте памяти) == >, memlocation = + memlocation, который написан на более высоком уровне язык, аналогично if (content in memory location) == "."тогда печать (содержимого памяти).

надеюсь, это все прояснит. tc