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