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

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

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

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

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


person speeder    schedule 30.05.2013    source источник
comment
Должно быть довольно сложно поддерживать приложение, написанное на этом языке.   -  person Breaking not so bad    schedule 30.05.2013
comment
@ ring0: нет, это язык только для записи.   -  person LetMeSOThat4U    schedule 20.03.2015
comment
в чем его практическая польза?   -  person Yash Kumar Verma    schedule 18.04.2016
comment
@YashVerma он не нужен ..   -  person Insane    schedule 18.04.2016
comment
@YashVerma Это четко указано в названии языка.   -  person Mateen Ulhaq    schedule 18.07.2016


Ответы (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, если вы используете оператор запятой ,, BF считывает 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? Ну 197 десятичное число c5 шестнадцатеричное. Знакомо? Конечно. Это первый байт ł!

3. Вывод

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

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

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

Если вы сейчас используете оператор точка (.), BF печатает:

a

Поскольку a десятичный код в ASCII - это 97.

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

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

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

aa

4. Петли

В BF цикл состоит из начала [ и конца ]. Вы можете подумать, что это как в 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)

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

person Scony    schedule 08.11.2013
comment
Круто, теперь понял :) - person speeder; 05.02.2014
comment
А как насчет , [ и ]? :( - person Josh M; 19.03.2014
comment
Вообще-то моей целью было просто объяснить идеологию bf, но вы правы. Скоро я уточню свой ответ. - person Scony; 26.03.2014
comment
Это было отличным решением для новичка, пытающегося понять идеологию этого языка. Поздравляю и отличный пост. - person Casey; 28.08.2014
comment
Лучшее вступление к Brainfuck, которое я когда-либо видел. Честно говоря вы немного отменили BF своим постом - person Boyang; 27.01.2016
comment
@Scony настоящие (мозговые) ублюдки не заботятся о Да Рулезе: $ echo "++++++++[>++++[>++>+++>+++>+<<<<-]>+>->+>>+[<]<-]>>.>>---.+++++++..+++.>.<<-.>.+++.------.--------.>+.>++." | bf Hello world! - person yyny; 08.03.2016
comment
Это лучший способ понять язык Brainf ***! Теперь я знаю, как мой собственный интерпретатор Brainf *** преобразует код в строку! - person Obinna Nwakwue; 14.03.2016
comment
Думаю, если вам нужен проект в свободное время, вы всегда можете добавить поддержку Unicode в Brainfuck. - person Álvaro González; 18.07.2016
comment
После вашего поста BF больше! BF! - person thanos.a; 16.04.2017

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

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

Статья в Wikipedia также идет более подробно.

Первая строка инициализирует 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 и т. Д.). После завершения цикла a[0] равно нулю. >++. затем перемещает указатель на a[1], который содержит 70, добавляет к нему два (получается 72, что является кодом символа ASCII заглавной H) и выводит его.

Следующая строка перемещает указатель массива на a[2] и добавляет к нему единицу, получая 101, строчную букву «е», которая затем выводится.

Поскольку 'l' оказывается седьмой буквой после 'e', ​​для вывода 'll' к a[2] добавляются еще семь (+++++++), и результат выводится дважды.

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

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

person ken    schedule 30.05.2013
comment
Но ПОЧЕМУ он печатает? или как? Комментарии объясняют мне цель линии, а теперь то, что она делает. - person speeder; 30.05.2013
comment
Он печатает, потому что компилятор знает, что , и . используются для ввода-вывода, так же, как печать C с использованием putchar. Это деталь реализации, обрабатываемая компилятором. - person ken; 30.05.2013
comment
А также потому, что он устанавливает в требуемых ячейках целочисленные значения для символов ASCII в Hello World. - person slugonamission; 30.05.2013
comment
Я ожидал более подробного объяснения ... но: / - person speeder; 31.05.2013
comment
@speeder - Я добавил в ответ подробное объяснение кода из Википедии. Вы можете увидеть связанную статью для получения дополнительной информации. - person ken; 31.05.2013

Brainfuck так же, как и его название. В нем используется всего 8 символов > [ . ] , - +, что делает его самым быстрым языком программирования, для изучения, но сложным для реализации и понимания. … .И заставляет вас, наконец, кончить тем, что трахает свой мозг.

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

let, изначально указатель, указывающий на ячейку 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, вы обнаружите, что это Здравствуйте писатель

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

——- Что-то еще ———

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

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

разбивая на части:

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

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

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. например, в python: chr (70 + 2) # печатает 'H'

>+.

переходит на 2-ю ячейку с приращением 1 к ее значению 100 + 1 и печатает ('.') ее значение, т.е. chr (101) chr (101) # печатает 'e', ​​теперь в следующем фрагменте нет> или ‹, поэтому он принимает текущее значение последнего элемента и только приращение к нему

+++++ ++..

последний элемент = 101, следовательно, 101 + 7 и печатает его дважды (так как есть два "..") chr (108) #prints l дважды можно использовать как

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

——— Где это используется? ——-

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

person DARK_C0D3R    schedule 11.01.2018

Чтобы ответить на вопрос, откуда он знает, что печатать, я добавил расчет значений 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)
person Rehana Mahfuz    schedule 20.03.2017

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

Пример:

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

write(1, ptr, 1)

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

person zeezor    schedule 18.07.2016

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

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

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

person Rahul    schedule 29.07.2018