Рубрика:
Разработка /
#10 лет назад
|
Facebook
Мой мир
Вконтакте
Одноклассники
Google+
|
АЛЕКСЕЙ ВТОРНИКОВ, ведущий программист, pdp8dec@gmail.com
Обманчивая простота языка BF: генезис или мутация?
Тема настоящей статьи – язык программирования, чье появление во многом обусловлено интеллектуальной «шалостью» его создателя. В отличие от большинства таких забав, быстро появляющихся и еще быстрее исчезающих, та, которой посвящена эта статья, благополучно живет и развивается
Обычно новый язык программирования является либо стремлением увеличить эффективность программирования (сравните: машинные коды → ассемблеры → Fortran → Algol → ...), либо как ответ на недостаточность выразительных средств существующих языков для решения сложных задач обработки информации – списков, графов, деревьев, логических выводов... (IPL-V, Lisp и функциональное программирование, Snobol, Prolog и др.). Бывает, новый язык программирования рождается в процессе исследовательской работы (например, SQL).
Немного истории
Язык программирования, о котором идет речь, никак нельзя назвать молодым – ему уже больше полутора десятков лет. В 1993 году швейцарский программист Урбан Мюллер (Urban Muller) задался целью разработать язык программирования с минимально возможным объемом компилятора (согласитесь, необычная обратная задача: разработать не компилятор для языка, а язык для компилятора так, чтобы размер последнего не превышал наперед заданного объема). Предполагаемый предельный объем компилятора составлял жалкие 1024 байта, но эта цель была перевыполнена: длина оригинального компилятора Мюллера, написанного на ассемблере для компьютера Amiga, составила всего240 байт. Правда, исходный язык программирования получился весьма необычным – кое-чем пришлось «пожертвовать».
Язык включал в себя всего восемь примитивных команд и требовал от программиста мобилизации серьезных интеллектуальных усилий при написании программ, из-за чего и получил недвусмысленное название Brainfuck. Переводить этоназвание с английского на русский я не рискну – оно звучит откровенно неприлично (по-английски, впрочем, тоже). Видимо, именно поэтому достаточно быстро оригинальное название языка сократили до BF.
Чтобы понять, почему язык получил такое необычное имя, давайте рассмотрим его описание. В отличие от подавляющего большинства других языков программирования, чьи описания нередко занимают сотни страниц, описание BF очень компактно. Это, разумеется, не означает, что программирование на BF простое занятие – как раз нет, но ограниченные рамки журнальной публикации не позволяют мне сколько-нибудь подробно останавливаться на этом, и поэтому заинтересованного читателя я отсылаю к ссылкам на ресурсы в конце статьи.
Среда исполнения и синтаксис BF
В распоряжении программиста имеется область памяти некоторого объема (исторически, обычно 215-1 ячеек, чего обычно вполне достаточно даже для весьма больших BF-программ). Графически удобно представлять область памяти какодномерный массив целых чисел, расположенный горизонтально (см. рис. 1).
Рисунок 1. Графическое представление область памяти
Доступ к ячейкам памяти осуществляется по указателю, подобно тому, как это делается в языке C. В каждый момент времени указатель «обозревает» одну ячейку памяти. Те, кто не знаком с языком C, могут представить память просто какмассив, а указатель как индекс массива (индексирование массива начинается с 0). Перед запуском программы на исполнение, все ячейки памяти инициализируются нулем, а указатель устанавливается на самую левую ячейку памяти.
В классическом варианте языка BF программы и данные разделены: память служит только местом хранения данных; это отличительная особенность так называемой «гарвардской» архитектуры. Однако ничто не препятствует тому, чтобы иданные, и программы использовали общую память (архитектура «фон Неймана»): для этого, разумеется, необходимо изменить интерпретатор. В статье, однако, рассматривается классический вариант языка BF.
Указатель может передвигаться вдоль памяти вперед (вправо) и назад (влево), последовательно переходя от одной ячейки к другой. Для этого в BF имеются соответствующие команды «>» и «<». В обозначениях языка C эти команды можно записать как «++pointer» и «--pointer» (здесь «pointer» – указатель на целое). Очевидно, что в силу ограниченности объема выделенной памяти необходимо следить за тем, чтобы указатель не выходил за границы отведенной памяти, ипредпринимать соответствующие меры.
Следующие две команды – всем хорошо знакомые команды увеличения и уменьшения. Первая из них («+») увеличивает на единицу значение, содержащееся в ячейке памяти под указателем, вторая («-») уменьшает на единицу значение вячейке памяти под указателем. На языке C это было бы записано как «++*pointer» и «--*pointer». Опять же, при реализации BF необходимо следить за переполнением значений в ячейках.
Для связи с внешним миром в языке BF имеются две команды. Первая из них «.» (точка) печатает на экране символ, чей ASCII-код хранится в ячейке памяти под указателем. Вторая команда «,» (запятая) приглашает к вводу символа склавиатуры; ASCII-код этого символа запоминается в ячейке памяти под указателем. Следующая (по-видимому, минимальная) программа на BF иллюстрирует эти команды:
,.
Первая команда ожидает ввода символа и сохраняет его ASCII-код в текущей ячейке памяти; указатель памяти остается без изменений. Вторая команда считывает число из текущей ячейки памяти, интерпретирует его как ASCII-код, переводит в соответствующее символьное представление и распечатывает.
К этому моменту были рассмотрены шесть из восьми команд BF: остались две последние. Эти команды позволяют управлять выполнением программы и всегда работают «в паре». Вот эти команды: «[» и «]».
Первая из них проверяет содержимое ячейки памяти под указателем и сравнивает его с нулем. Если содержимое этой ячейки равно 0, то управление выполнением программы передается на команду за парной ей скобкой «]». Если не 0, товыполняются команды, следующие после этой скобки вплоть до парной «]». Так реализуется передача управления вперед.
Теперь о второй команде. Если содержимое ячейки под указателем памяти не 0, то управление передается назад, к первой команде после парной «[». Если же содержимое памяти под указателем 0, то выполняется команда, следующая за «]». Таким образом, эти две команды реализуют цикл, который подобен конструкции «while» знакомого по языкам программирования C или Java: цикл выполняется пока не 0.
Выше я говорил о том, что команды «[» и «]» всегда «работают» совместно. Иными словами, точно так же, как и в арифметическом выражении, скобки «[» и «]» должны быть правильно вложены (сбалансированы).
Ниже приведен пример правильного вложения:
[[][[[]]]]
А вот эти последовательности команд «[» и «]» вложены неправильно:
[[][]]]
[[[][]]
К счастью, баланс скобок «[» и «]» легко проверяется на этапе компиляции (см. ниже).
Вот, собственно, и все описание языка BF. Внимательный читатель, безусловно, спросит, а как быть с символами, не входящими в набор указанных восьми? А никак – они попросту игнорируются. Отсюда следует, что любой текст, заисключением указанных восьми символов, можно рассматривать как комментарий. Это весьма удобно, в чем читатель, безусловно, быстро убедится, когда будет составлять свои программы или разбирать программы, написанные другими. Чтобы читатель мог составить представление, как выглядит типичная BF-программа, привожу исходный код, печатающий каноническое «Hello World!»:
>+++++++++[<++++++++>-]<.>+++++++
[<++++>-]<+.+++++++..+++.[-]>++++
++++ [<++++>-]<.>+++++++++++[<++
+++>-]<.>++++++++[<+++>-]<.+++.--
----.--------.[-] >++++++++[<++++>
-]<+.[-]++++++++++.
Попытайтесь вручную «прокрутить» эту программу. Теперь стало понятней, почему язык получил свое имя?
Интерпретатор
Ниже приводится исходный код интерпретатора BF, написанный на Java. Для ясности восприятия исходный код несколько упрощен (в частности, не предпринимаются никакие действия при выходе за границы памяти и переполнении). Тех читателей, которые хотели бы написать свой интерпретатор или посмотреть его реализацию на других языках программирования, отсылаю к ссылкам в конце статьи (кстати, я сам оттуда же позаимствовал многие идеи и приемы).
Учитывая простоту синтаксиса BF, интерпретатор реализован что называется «в лоб».
В точке входа (метод main()) считывается имя файла с исходным текстом BF-программы. Содержимое файла сохраняется в строку, из которой удаляются все символы, не относящиеся к языку BF (метод cleanCode()); после такой «очистки» интерпретировать программу гораздо проще. Метод checkBrackets() сканирует эту строку и проверяет в нем баланс скобок «[» и «]» стандартным методом счетчика.
Если скобки сбалансированы, то инициализируются область памяти, программный счетчик и указатель памяти, после чего начинается интерпретация кода (метод interpret()). Интерпретатор считывает команду за командой, передает управление соответствующему методу и увеличивает значение программного счетчика.
Обратите внимание на методы startJump() и endJump(): их назначение – поиск в исходном коде парных скобок (соответственно, поиск «]» и «[») и корректировка значения программного счетчика (причем корректировка может происходить как в сторону увеличения, так и уменьшения – все зависит от того, какая скобка ищется). Переменная level отслеживает уровень вложенности и помогает искать парную скобку.
Заключительные замечания
Разумеется, язык BF абсолютно не практичен, но если вы ищете способ изысканно и нестандартно сойти с ума, то трудно найти что-либо сравнимое. Замечу, что с математической точки зрения язык BF полон по Тьюрингу, то есть приналичии достаточного объема памяти с его помощью можно вычислить все то, что может вычислить машина Тьюринга. И, следовательно, возможности языка BF не слабее возможностей любых других языков программирования, таких, кпримеру, как ассемблер, C, Java и прочее. В это непросто поверить, но математика, из которой следует заключение (теория вычислимости и рекурсивных функций), не нуждается в вере. Язык BF, при всей его непрактичности, может служить примером (причем, еще не самым простым) так называемых «базисов» – минимальных конструкций, используемых для исследования алгоритмов и построении теоретических моделей вычислимых функций. Это, однако, совсем другая история.
Напоследок (для тех, у кого остаются сомнения в возможностях языка BF) привожу результат работы BF-программы, строящей фрактальное множество Бенуа Мандельброта (см. рис. 2).
Рисунок 2. Фрактальное множество Бенуа Мандельброта, построенное BF-программой
Вот, собственно, и все. Если читатель имеет склонность к решению сложных задач, то язык BF, пожалуй, способен удовлетворить такой взыскательный вкус. Желаю этому читателю успехов и открытий в освоении такого удивительно простого и одновременно такого сложного языка программирования, как BF.
- http://en.wikipedia.org/wiki/Brainfuck – общее введение в BF с иллюстративными примерами.
- http://www.nettwerked.net/K-1ine_44.txt – прекрасное описание BF, написанное программистом и для программистов.
- http://esoteric.sange.fi/brainfuck – богатая коллекция интерпретаторов, компиляторов и исходных текстов BF-программ.
- http://community.livejournal.com/ru_brainfucker – русскоязычное сообщество по BF. Много интересных и полезных сведений не только по BF, но и по другим языкам программирования.
- http://bf.kzn.ru – сайт на русском языке, посвященный языку BF. Здесь вы найдете интересную информацию.
- http://mozaika.com.au/oleg/zhur/mozg10r1.html – ссылка для настоящих любителей вывернуть мозг наизнанку. В статье О. Мазонки и Д. Кристофани рассматривается и исчерпывающим образом разбирается самоинтерпретатор языка BF.
Ключевые слова: язык программирования, BF, Brainfuck.
Facebook
Мой мир
Вконтакте
Одноклассники
Google+
|