Brainfuck — один из эзотерических языков программирования, придуман Урбаном Мюллером (нем.Urban Müller) в 1993 году, известен своим минимализмом. Название языка можно перевести на русский как вынос мозга, оно напрямую образовано от английского выражения brainfuck (brain — мозг, fuck — сношать), т. е. заниматься ерундой. Язык имеет восемь команд, каждая из которых записывается одним символом. Исходный код программы на Brainfuck представляет собой последовательность этих символов без какого-либо дополнительного синтаксиса.
Одним из мотивов Урбана Мюллера было создание языка с как можно меньшим компилятором. Отчасти он был вдохновлён языком FALSE, для которого существовал компилятор размером 1024 байта. Существуют компиляторы языка Brainfuck размером меньше 200 байт[1]. Программы на языке Brainfuck писать сложно, за что его иногда называют языком для мазохистов. Но при этом Brainfuck является вполне естественным, полным и простым языком и может использоваться при определении понятия вычислимости.
Машина, которой управляют команды Brainfuck, состоит из упорядоченного набора ячеек и указателя текущей ячейки, напоминая ленту и головку машины Тьюринга. Кроме того, подразумевается устройство общения с внешним миром (см. команды . и ,) через поток ввода и поток вывода.
Язык Brainfuck можно описать с помощью эквивалентов языка Си:
Команда Brainfuck
Эквивалент на Си
Описание команды
Начало программы
int i = 0; char arr[30000]; memset(arr, 0, sizeof(arr));
выделяется память под 30 000 ячеек с нулевыми начальными значениями
>
i++;
перейти к следующей ячейке
<
i--;
перейти к предыдущей ячейке
+
arr[i]++;
увеличить значение в текущей ячейке на 1
-
arr[i]--;
уменьшить значение в текущей ячейке на 1
.
putchar(arr[i]);
напечатать значение из текущей ячейки
,
arr[i] = getchar();
ввести извне значение и сохранить в текущей ячейке
[
while(arr[i]){
если значение текущей ячейки ноль, перейти вперёд по тексту программы на символ, следующий за соответствующей ] (с учётом вложенности)
]
}
если значение текущей ячейки не нуль, перейти назад по тексту программы на символ [ (с учётом вложенности)
Несмотря на внешнюю примитивность, Brainfuck с бесконечным набором ячеек имеет тьюринговскую полноту, а, следовательно, по потенциальным возможностям не уступает «настоящим» языкам, подобным Си, Паскалю или Java.
Brainfuck подходит для экспериментов по генетическому программированию из-за простоты синтаксиса, и, соответственно, генерации исходного кода.
В «классическом» Brainfuck, описанном Мюллером, размер ячейки — один байт, количество ячеек 30 000. В начальном состоянии указатель находится в крайней левой позиции, а все ячейки заполнены нулями. Увеличение или уменьшение значений ячеек происходит по модулю 256. Ввод-вывод также происходит побайтно, с учётом кодировки ASCII (то есть в результате операции ввода (,) символ 1 будет записан в текущую ячейку как число 0x31 (49), а операция вывода (.), совершённая над ячейкой, содержащей 0x41 (65), напечатает латинскую А). В других вариантах языка размер и количество ячеек может быть другим (бо́льшим). Есть версии, где значение ячеек не целочисленно (с плавающей точкой).
Пошаговая программа на языке Brainfuck, печатающая «Hello World!» с переносом строки (в виде ASCII-кода - 72 101 108 108 111 32 87 111 114 108 100 33 10):
Итого 389 операторов и использована 1 ячейка памяти. Оптимизированная программа заметно короче - всего 111 операторов, но 5 ячеек памяти. Первая ячейка используется как обратный счётчик цикла на 10 итераций, в последующих ячейках находятся числа 7, 10, 3 и 1, наращиваемые этим циклом до 70, 100, 30 и 10, досуммирование происходит перед печатанием, второе слово конструируется из остатков первого:
Обозначим через @(k) сдвиг на k ячеек вправо, если k>0, и влево, если k<0
Соответственно, @(k) = >…k раз…> либо <…-k раз…< zero(): обнуление текущей ячейки:
[-]
=
[+] add(k): прибавление значения ячейки n (текущей) к значению ячейки n+k:
[ — @(k) + @(-k) ]
при этом значение ячейки n теряется (обнуляется). mov(k): копирование значения ячейки n (текущей) в ячейку n+k с потерей (обнулением) значения ячейки n:
@(k) zero() @(-k) add(k)
=
@(k) [-] @(-k) [ — @(k) + @(-k) ] copy(k,t): копирование значения ячейки n (текущей) в ячейку n+k
c использованием промежуточной ячейки n+k+t, благодаря чему значение ячейки n не теряется (сохраняется).
@(k) zero() @(t) zero() @(-k-t) [ — @(k) + @(t) + @(-k-t) ] @(k+t) mov(-k-t)
=
@(k) [-] @(t) [-] @(-k-t) [ — @(k) + @(t) + @(-k-t) ] @(k+t) [ — @(-k-t) + @(k+t) ] ifelse(t): если текущая ячейка>0, то выполняется условие true
если текущая ячейка=0, то выполняется условие false
t-относительный номер вспомогательной ячейки:
@(t)[-]+@(-t) устанавливаем флаг 1 для случая else
[
здесь действия ветки true
@(t)[-]@(-t) устанавливаем флаг 0 для случая else
[-] выход из цикла
]
@(t)
[@(-t)
здесь действия ветки false
@(t)[-] выход из цикла
]
@(-t-1)
Brainfuck почти не используется для практического программирования (за исключением работ отдельных энтузиастов), а используется преимущественно для головоломок и задач для соревнований.
Примечания: 1. Специально для функционала команды mOO в язык COW введены внутренние коды его команд[2], в таблице они указаны в отдельном столбце. 2. Отсутствие команды обозначается отс.
Brainfuck
Ook!
COW
код COW
Описание
]
Ook? Ook!
moo
0
Конец цикла
<
Ook? Ook.
mOo
1
Предыдущая ячейка
>
Ook. Ook?
moO
2
Следующая ячейка
отс.
отс.
mOO
3
Выполнить значение в текущей ячейке как команду с соответствующим кодом из диапазона 0 - 11; код 3 вызывает зацикливание
отс.
отс.
Moo
4
Если значение текущей ячейки равно нулю, то ввести его с клавиатуры; если же значение текущей ячейки — не ноль, то вывести его на экран
-
Ook! Ook!
MOo
5
Значение текущей ячейки уменьшается на 1
+
Ook. Ook.
MoO
6
Значение текущей ячейки увеличивается на 1
[
Ook! Ook?
MOO
7
Начало цикла (у COW есть особенность - пропускается первая команда цикла)
[-]
отс.
OOO
8
Обнуляет значение в текущей ячейке
отс.
отс.
MMM
9
Если регистр пуст, скопировать в него значение текущей ячейки, иначе скопировать в ячейку содержимое регистра и очистить регистр
Ook.jar, another Ook! and Brainfuck language interpreter, this time written in Java.
BrainForceАрхивная копия от 31 марта 2012 на Wayback Machine, compiler (wrap gcc) and C translator (has lots of options to control wrapping values, cell sizes, etc.)
esolang, a Brainfuck interpreter for iPhone written in objective c.
Le Brainfuck, a Javascript based optimizing interpreter. Also has many options, including memory dumping.
Brainfuck C, a lightweight Brainfuck interpreter written in C.
Brainfuck Java, an interpreter for the Brainfuck language and its dialects written in the Java programming language.