]]> ]]>
Править | Обсудить | История

Befunge

Дата создания:
1993
Создан под влиянием:
Парадигма:
Типизация:
Реализации и версии (свернуть все | развернуть все):
Язык программирования

Befunge — один из самых старых и самых известных двухмерных эзотерических языков программирования.

Befunge был придуман в 1993 Крисом Пресси (Chris Pressey). Он задался целью создать язык с максимально сложной компиляцией. В последующие годы появился ряд реализаций этого языка, а также несколько диалектов и модификаций под общим названием “фунгеоиды”. Два главных диалекта языка — оригинальный Befunge-93 и более поздний Befunge-98.

Исходная спецификация языка ограничивала максимальный размер поля (80x25), поэтому в отличие от многих других эзотерических языков, Befunge — не тьюринг-полный язык. Так же, как и другие эзотерические языки, он не имеет практической ценности.

Программа на Befunge — двухмерное поле фиксированного размера. Изначально его ячейки заполнены командами программы. Befunge позволяет модифицировать код программы в процессе ее выполнения, поэтому ячейки поля можно использовать и в качестве памяти произвольного доступа (правда, с ограниченными возможностями: хранить можно только числа до 255). Поле сворачивается в тор: при переходе через его край указатель инструкций попадает на противоположный край, сохраняя при этом свое направление.

Программа выполняется при помощи счетчика программы (диалект -93) или указателя инструкций (-98). В начале выполнения он находится в верхней левой клетке поля и направлен вправо. Он двигается в направлении, в котором он указывает, на одну ячейку за раз. Когда указатель попадает в клетку с командой, она выполняется. Команды могут влиять на направление указателя (в Befunge-98 добавлены инструкции, которые могут изменять и положение указателя).

Основной структурой данных в Befunge является стек; все операции используют его для получения аргументов и записи результатов. Единственный тип данных — целое число. Символы можно использовать для ввода/вывода данных и для более удобного представления строковых констант внутри самой программы; при этом они обрабатываются как соответствующие ASCII-коды.

В Befunge-93 определен следующий набор команд:

  • + — * / % Выполнить соответствующую операцию над двумя верхними элементами стека; нижний элемент стека используется в качестве первого операнда, верхний — в качестве второго
  • ! Логическое НЕТ
  • ` Больше, чем
  • < > ^ v Изменить направление указателя инструкций: влево, вправо, вверх, вниз
  • ? Изменить направление указателя инструкций на случайное
  • _ | Горизонтальное/вертикальное ЕСЛИ. Обе команды извлекают верхний элемент из стека; если он равен 0, выполняется команда > v, иначе выполняется < ^
  • " Переключить строковый режим. В строковом режиме каждый встреченный символ добавляет свой ASCII-код в стек
  • : Продублировать верхний элемент стека
  • \ Поменять местами два верхних элемента стека
  • $ Удалить верхний элемент стека
  • . , Вывести верхний элемент стека как число/как символ
  • # Мост: пропустить следующую команду
  • g Получить значение в ячейке программы про ее координатам
  • p Поместить значение в ячейку программы с заданными координатами
  • & ~ Прочитать число/символ и поместить его (или его ASCII-код) в стек
  • 0..9 Поместить соотвествующее значение в стек
  • @ Закончить выполнение программы

При работе команд g и p с координатами ячейки из стека вначале извлекается y-координата (строка), затем — x-координата (столбец). Обе координаты начинаются с 0 (так, (0, 0) — верхняя левая ячейка программы). Команда p, которая записывает значение в ячейку программы, извлекает его из стека после координат ячейки.

Примеры:

Hello, World!:

Пример для версий befungee 0.2.0

Первая часть примера (до пробела) размещает в стеке нужные значения. 25* помещает в стек значение 10 (ASCII-код перевода строки), затем " переводит программу в строковый режим: каждый следующий символ (до вторых кавычек) добавляет в стек свой ASCII-код.

Вторая часть примера — цикл, выводящий на печать все значения в стеке, начиная с самых верхних. > возвращает указатель инструкций к движению вправо (после конца одной итерации). : копирует верхний элемент стека (т.е. текущий символ), из-за моста # выполняется только при движении вправо. , выводит верхний элемент стека (выполняется только при движении влево). _ пропускает указатель инструкций к @ (концу программы), если верхний элемент стека — 0, и отражает его влево в противном случае. Последовательность действий в одной итерации следующая:

  • скопировать верхний элемент стека,
  • проверить, равен ли он 0; если равен, выйти из цикла (при этом в любом случае копия элемента удаляется из стека),
  • напечатать верхний элемент стека.
25*"!dlroW ,olleH" >:#,_@

Hello, World!:

Пример для версий befungee 0.2.0

Этот пример более прямолинеен — ASCII-коды символов заносятся в стек в численном виде, и символы выводятся на печать сразу после попадания кодов в стек, так что цикл не нужен.

89*,45*99*+,39*99*+::,,56*99*+,29+4*,48*,92+8*1-,56*99*+:,3+,,25*:*,56*3+,25*,@

Факториал:

Пример для версий befungee 0.2.0

Программа состоит из двух циклов — первый вычисляет числа от 16 до 1, второй — собственно факториалы.

Прежде всего в стек заносится 16 (как 4*4) — максимальное число, факториал которого нужно вычислить. Затем следует первый цикл, выполняющийся по часовой стрелке. За одну итерацию в стек дописывается число, равное предыдущему, уменьшенному на 1. Когда это число становится нулем, цикл прерывается (_ во второй строке), и указатель инструкций направляется вправо.

Перед входом во второй цикл из стека удаляется верхний элемент (0) и добавляется 1 (текущее значение факториала). Затем следует второй цикл, выполняющийся против часовой стрелки. За одну итерацию происходит следующее:

  • \ — два верхних элемента (ранее вычисленный факториал и следующее число) стека меняются местами (число становится верхним элементом). Если в стеке всего один элемент, поверх него записывается 0.
  • :_ — если следующее число — 0 (или стек пуст), цикл прерывается.
  • . — следующее число выводится на печать (копия остается в стеке).
  • блок ,,,,"! = " — помещает в стек символы строки и сразу же их выводит. Строка проходится справа налево, но и символы выводятся от самого верхнего к нижним, так что для программиста строка выглядит так же, как на печати.
  • .:* — вычисляется новый факториал и выводится на печать (копия остается в стеке).
  • ,*25 — вывод перевода строки.
44* >:1-:v    v ,*25 .:* ,,,,"! = ".:_ @ 
    ^    _ $1 > \:                   ^ 

Числа Фибоначчи:

Пример для версий befungee 0.2.0

Используется итеративное определение чисел Фибоначчи.

В этом примере используются команды p и g, предназначенные для модификации исходного кода программы в процессе выполнения, — они записывают заданный символ в заданную клетку программы и считывают его оттуда в стек, соответственно. В данном случае это используется для хранения вычисленных чисел Фибоначчи (счетчик цикла все время хранится в стеке). При записи чисел как ASCII-кодов символов большие числа искажаются, поэтому выводятся только первые 13 штук.

031p132p 94+ > 31g 32g :. + 32g v
             | :-1,,", "p23 p13 <
             > "."::,,,25*,@

CamelCase:

Пример для версий befungee 0.2.0

Программа реализует посимвольную обработку введенной строки. Код снаружи цикла 152p записывает в ячейку программы (5,2) значение 1, соответствующее логическому true — начало строки считается пробелом.

Основная часть программы — цикл. В одной итерации цикла считывается (~) и обрабатывается один символ. Выход из цикла осуществляется, если ASCII-код введенного символа равен 10 (: 25*- #v_ @), в противном случае указатель инструкций отправляется во вторую строку (команда v). Если символ — буква нижнего регистра, он переводится в верхний — для единообразия последующей обработки (часть программы до первого квадратного блока и сам блок). Затем аналогичным способом проверяется, является ли символ буквой верхнего регистра. Если нет (переход в первую строку), в ячейку (5,2) снова записывается 1, символ удаляется из стека, и цикл заканчивается. Если да (переход в третью строку), выполняется еще одна проверка — на значение ячейки (5,2), в зависимости от которого символ остается собой или переводится в нижний регистр. Наконец, символ выводится, и цикл заканчивается.

152p > ~ : 25*- #v_ @               >48*-v                    >152p $        v
                 > :: "`"` \"{"\` * |    > :: "@"` \"["\` * ! |    >    v
                                    >    ^                    >52g |    >052p,v
                                                                   >48*+^
     ^                                                                        <
 ( )
  |
  was last character a space? (5,2)

Комментарии

]]>

blog comments powered by Disqus

]]>

Работа программистам