Журнал LinuxFormat - перейти на главную

LXF90:Лингва эзотерика

Материал из Linuxformat
Перейти к: навигация, поиск
Нестандартное программирование

Содержание

Лингва эзотерика

По-вашему, C – это сложно? А у Perl нехороший синтаксис? Хватит ныть, вот сейчас Майк Сондерс покажет вам, что такое действительно трудный язык…

Однажды Свами Прабхупада [Swami Prabhupada], основоположник движения Харе Кришна, прогуливаясь с послушниками около поля для гольфа, увидел игрока, замахнувшегося для очередного удара. «Поглядите на этого человека, – сказал Прабхупада, – всю свою жизнь он трудился, но это ничто по сравнению с усилиями, которые он прилагает сейчас, чтобы забить шар в лунку».

Такова уж людская природа: вечно нам подавай преодоление каких-то препятствий, надо или не надо. Есть же превосходные языки высокого уровня – C#, Ruby и Python, а разработчики все норовят исследовать что-то новое, и в результате появляются совсем уж причудливые (но любопытные!) создания.

В предыдущих статьях рассказывалось о Tcl, Ruby, хоть и не слишком часто применяемых, но достойных изучения хотя бы ради расширения профессионального кругозора. А сегодня мы займемся по-настоящему экзотическими языками, настолько забавными и нетривиальными, что вы увидите ремесло программирования в совершенно новом свете. Необходимые программы находятся в разделе Magazine/Esoteric нашего DVD.

Мы предполагаем, что вы владеете базовыми понятиями в этой области; но даже те, кто сроду кода не писал, сочтут некоторые концепции неплохой гимнастикой для ума. Вы вряд ли выберете эти языки для создания крутого пакета офисных программ, зато, изучая их, получаешь глубокое представление о строении языка программирования. Да и С после них кажется малиной…

Вьем петли из Spaghetti

Начнем с несложного языка со вкусным именем Spaghetti. В нем каждая строка кода заканчивается оператором перехода GOTO, причем переход на следующую строку запрещен. Иными словами, две соседние строки кода никогда не выполняются последовательно. Рациональный код и дурак напишет, а мы попрыгаем!

Почти все книги по программированию твердят, что оператор GOTO лучше не использовать, обходясь циклами и вызовами функций. Код Spaghetti, наоборот, петляет как заяц, на радость тем, кому циклы, функции и оператор return кажутся простецкими. Spaghetti – интерпретируемый язык. После запуска первые 256 байт выделенной программе оперативной памяти содержат числа от 1 до 255; есть еще 26 переменных от a до z, которые находятся в ячейках с 256 по 281. Двойное слово в паре регистров a и b может использоваться как указатель на некоторую ячейку памяти. Например, установив содержимое обоих регистров равным 255 (FF в шестнадцатеричном виде), мы укажем на ячейку памяти 65535. Сокращенно этот составной адрес ячейки памяти обозначается *.

Займемся командами. Команда a? вводит символ и записывает его в переменную a, а ?a выводит на экран значение переменной a. Вместо a, естественно, можно использовать любую переменную из диапазона a–z. Каждая строка кода начинается с ее номера, а заканчивается номером строки, куда нужно перейти (ни в коем случае не к следующей!). Выполнение программы начинается с первой строки и заканчивается при переходе на нулевую строку.

Оператор условия выглядит следующим образом: a~b:5. Если a равно b, переходим на пятую строку программы, в противном случае – на строку, номер которой указан в конце данной строки. Номера строк отделяются от собственно операторов квадратными скобками. Так, 1[a~b:3]4 означает «если a равно b, перейти к строке 3, а если нет, то к строке 4».

Пример простенькой программы:

2[x~13:0]3
1[x?]2
3[?x]1

Кристально неясно, да? Повесим на уши Spaghetti: программа просто выводит на экран символы, вводимые пользователем, и завершается при нажатии Enter. Выполнение начинается со строки 1, где с клавиатуры вводится символ, который сохраняется в переменной x. Затем велено перейти к строке 2 (ну нельзя же написать ее следом – она находится над строкой 1).

Тут переменная x, содержащая введенный символ, сравнивается с кодом клавиши Enter (13). Если x равно 13, переходим к нулевой строке – это, как вы помните, означает выход из программы. Если нет, переходим к строке 3 и выводим символ на экран. После этого снова возвращаемся к строке 1.

Посмотрите на текст программы еще раз, и вы получите мало с чем сравнимое удовольствие своих серых клеточек. Парочка программ на языке Spaghetti, и вы с легкостью читаете программы на Basic, которые прежде напоминали средневековые манускрипты. Ну, это… если предварительно не рехнетесь.

«Ты туда не ходи, ты сюда ходи»

Spaghetti требовал использования оператора GOTO, а Come Here, наш следующий язык программирования, применяет оператор COME FROM. Пусть в программе встретилось COME FROM 30; тогда интерпретатор после строки 30 выполнит код, следующий за COME FROM. Это означает, что вам нипочем не понять логику программы, пока вы не дочитаете ее до самого конца.

Вот программа, которая в бесконечном цикле выводит на экран слово ‘Developers’ – прямо виртуальный Стив Балмер (кстати, язык Come Here чувствителен к регистру символов):

COME FROM 10
10 TELL “ Разработчики” NEXT

Сначала выполняется строка 10, где командой TELL на экран выводится ‘Разработчики’. NEXT отмечает конец команды. Затем происходит прыжок к оператору COME FROM 10 (раз только что была выполнена строка с номером 10), после которого выполняется опять строка 10, и так далее. Номер строки не обязателен: он играет роль метки.

Следующая программа десять раз подряд выводит на экран строку ‘Wowzers’.

10 CALL 10 counter
COME FROM 10 + SGN counter
TELL “Wowzers” NEXT
11 CALL counter - 1 counter

В строке 10 оператором CALL переменной counter присваивается значение 10. Оператор COME FROM в следующей строке получает управление из строки с номером 10 + SGN counter, где ‘SGN counter’ – это знак переменной counter (счетчик), равный единице, если переменная положительна. То есть пока счетчик больше нуля, из строки 11 будет делаться переход к оператору COME FROM. В этой строке значение счетчика уменьшается на единицу; когда оно станет равным нулю, переходов к оператору COME FROM больше не будет. Итого: в строке 10 устанавливается значение счетчика, затем выводится на экран строка ‘Wowzers’, в строке 11 значение счетчика уменьшается на единицу, после чего переходим к оператору COME FROM, строка вновь выводится на экран и т.д., до тех пор, пока значение переменной counter больше нуля. Просто, не правда ли?

Неформальный Befunge

При всей своей странности, Come Here все-таки придерживается привычного размещения операторов по отдельным строкам. Но взгляните на исходный код любой программы – короткие и пустые строки и пробелы оставляют очень много места. А ведь размер текстового терминала 80x25 символов, почему бы не повысить его КПД?

Пытаясь создать язык, сложный для компилирования, Крис Пресси [Chris Pressey] в 1993 г. придумал Befunge. Вместо построчного кода, программа работает с двумерной сеткой и может переходить в любом из четырех направлений. Язык основан на концепции стека.

Напомним: стек – это блок памяти произвольного размера, учитывающий порядок записи в него. Как тубус для теннисных мячей: доступен только верхний мячик, и вложить мячи можно тоже только сверху. Например, если поместить в стек числа 3, 7 и 15, прочитаем их мы в обратном порядке: 15, 7 и 3 (последним вошел, первым вышел – по-английски сокращенно LIFO). Стек – удобное средство временного хранения данных, тем более в Befunge: переменных в этом языке нет.

Управление выполнением программы осуществляется с помощью операторов ^ (вверх), v (вниз), < (налево) и > (направо). Если в строке кода встречается число или символ, они помещаются в стек. Команда «запятая» (,) производит чтение из стека. Доступны арифметические операции +, -, / и * (сложение, вычитание, деление и умножение). Вот как выглядит программа Hello World на Befunge:

>                                    v
v      , , , , , “ H e l l o “ <
>4 8 * ,                             v
v , , , , , , “ Wo r l d ! “ <
>2 5 * , @

Попробуем разобраться. Выполнение программы всегда начинается с левого верхнео угла, там командой '> задан переход вправо. Интерпретатор перемещается до появления следующей команды v и начинает движение вниз. Следующий оператор < вызывает движение влево, и начинается работа.

Мы натыкаемся на строку Hello, и ее символы помещаются в стек в обратном порядке (olleH). Серия команд “,” по очереди считает их из стека и выведет на экран.

Затем мы движемся вниз (v) и направо (>), где помещаем в стек числа 4 и 8, вычисляем их произведение с помощью оператора * и выводим результат на экран. Результат, 32, это код символа пробела, он-то и выведется. Затем мы движемся вниз и налево. На экран выводится вторая строка World!.

Наконец, мы заносим в стек 2 и 5, вычисляем их произведение – 10 и выводим на экран символ с кодом 10 (переход на новую строку). Тут интерпретатор натыкается на команду завершения работы – @.

В Befunge еще много интересного. Ввод значений с клавиатуры и запись их в стек осуществляется с помощью операторов & и ~ соответственно для числовых и символьных величин. Можно поменять местами два элемента на вершине стека оператором \ или пропустить очередную команду оператором #. А самый захватывающий оператор – ?, он осуществляет случайный выбор направления перехода (на все четыре стороны), в котором продолжится выполнение программы. Это может пригодиться для рандомизации программы, зато уж понять ее будет практически невозможно.

Malbolge: PHP от дьявола?

Дальше – больше. Язык Malbolge, получивший свое название от восьмого круга ада [Malebolge] из «Божественной комедии» Данте, считается самым сложным языком программирования за всю историю. Уж если вы его освоите, значит, вам по силам левитировать на три фута над землей или манием руки вызывать грозу. Кстати, автор выдаст 100 фунтов тому, кто напишет на Malbolge клона доктора Элизы [Eliza] из Emacs (m-x-doctor).

С чего это мы расщедрились? Судите сами. Первая программа на языке Malbolge появилась только через два года после его создания – да и написал-то ее не человек, а компьютер. Malbolge настолько сложен, что простые смертные справляются в лучшем случае с выводом строки текста. Даже программы, созданных компьютером, на большее особо не замахиваются. И это плохо, скверно, не здорово.

Интерпретатор языка Malbolge представляет собой тернарный процессор. Если вы программировали на языке Assembler, то знакомы с двоичной (основание 2) и шестнадцатеричной (основание 16) системами счисления. Троичная система счисления исключительно неуклюжа, а Malbolge того и надо. Посчитайте: 0, 1, 2, 10, 11, 12, 20, 21, 22, 100… Ужас.

Но это еще цветочки. Пользователю доступны три регистра (аналог переменных) A, C и D. Регистр A – аккумулятор, он хранит результаты выполнения операций; регистры C и D содержат адреса ячеек памяти для кода и данных соответственно, причем после выполнения очередной команды их содержимое увеличивается на единицу.

Доступны 59049 ячеек памяти, каждая из которых может содержать десятиразрядное троичное число.

Восемь основных команд языка включают команды перехода, троичного сдвига, ввода/вывода и завершения программы. Есть еще «шальная» команда, по двум операндам выбирающая значение из таблицы – так Malbolge заполняет неиспользованную память по данным использованной.

Когда интерпретатор начинает выполнение очередной команды, к содержимому регистра C (скажем, 4) прибавляется смещение этой команды в коде программы (например, 390). Затем из полученного числа вычитается 94 до тех пор, пока результат больше 94. Результат этой операции определяет код выполняемой команды. После выполнения команды содержимое соответствующей ячейки памяти шифруется при помощи таблицы подстановки, и переход на эту ячейку станет невозможен.

Вот и все, что мы можем открыть, а не то журнал вспыхнет у вас в руках – о прочей черной магии узнаете через ссылки в нашей врезке Ресурсы, ниже на этой странице. В заключение – программа, выводящая на экран строку “Hello, world” на языке Malbolge:

(=<`:9876Z4321UT.-Q+*)M’&%$H”!~}|Bzy?=|{z]KwZY44Eq0/{mlk**
hKs_dG5[m_BA{?-Y;;Vb’rR5431M}/.zHGwEDCBA@98\6543W10/.
R,+O<

Нигилизмы

Радуйтесь: даже в мире экзотических языков программирования бывают передышки на что-то менее безумное. Разработчики Nil применили свежий подход к программированию, поставив дизайн впереди программных ухищрений. Много говорено, что языки высокого уровня типа C# избавляют от рутинных операций, позволяя сосредоточиться на алгоритме, а Nil превращает этот процесс в настоящее искусство.

Nil дает существенное уменьшение размера получаемого объектного кода, упрощение отладки программы и полную межплатформную переносимость. Это один из немногих языков, которые, пообещав эти преимущества, в самом деле обеспечивают и их, и многое другое!

Формат исходного кода языка может варьироваться. Рассмотрим фрагмент кода:

 for(b = 1; bytes < c_byte; b++) {
    if(!(bh[b] = sb_getblk(s, ++cur_index)))
        GOTO block_release;
    bytes += msBlk->devblksize;
 }

Здесь содержатся операторы цикла и сравнения. Как этот код оптимизировать? Компилятор языка Nil расщепляет программу на «атомы», удобные для синтаксического анализа. Каждый «атом» транслируется отдельно; Nil практикует нигилистический подход, согласно которому «атомарная» операция есть ни что иное как ничто, т.е. nop, и выполняет соответствующие подстановки.

С позиций данной оптимизации, Nil сворачивает последовательность команд nop в единственную команду nop, существенно уменьшая время выполнения программы и размер используемой памяти. Команда nop поддерживается подавляющим большинством процессоров, поскольку рекомендует им не делать ничего. Итак, код приведенного фрагмента кода на языке Nil будет выглядеть следующим образом:

 nop

нигилисты ведь утверждают, что все на свете бессмыссленно! Зачем процессору париться с каким-нибудь преобразованием Фурье? Пусть постынет, пока вы пьете чаек!

Здесь смотреть нечего

В статье про экзотические языки нельзя не упомянуть о Whitespace. Этот язык программирования, команды которого состоят из пробелов, символов табуляции и перевода строки, не видимых на экране, по праву можно считать самым чудаковатым. Открыв его программу в текстовом редакторе с подсветкой синтаксиса, вы увидите одни разноцветные пятна.

Тем не менее это полноценный язык. Например, команде вычитания соответствует последовательность из символа табуляции, двух пробелов и еще одного символа табуляции. Как и в языке Befunge, широко используется стек. Вы можете создавать процедуры и пользоваться метками. Учтена даже модульность, столь важная для крупных проектов!

Рассмотрим пример программы:



Здесь мы складываем два числа и записываем результат в память. Разработчики уверяют, что язык очень популярен для приложений с высокой защищенностью: даже распечатав текст программы, злоумышленник не увидит в ней уязвимостей. Кстати, распечатка сберегает окружающую среду, экономя на чернилах. Чем не язык для пост-структуралистов?

Программа

Программы на Whitespace видимы только в редакторах с подсветкой синтаксиса.

C: чего только не придумают

Одна из бед языка C заключается в том, что здесь не выработан стандартный стиль программирования (в отличие, например, от Python). На этом языке вполне можно писать читаемые программы, но уж слишком легко все запутать отступами, пробелами, именами переменных, макросами и прочим. Если вам приходилось разбираться в чужом коде, вы наверняка не раз проклинали все на свете, недоумевая: «Ну что он хотел этим сказать?».

К счастью, теперь вам есть куда послать нечитаемый код. Каждый год участники конкурса International Obfuscated C Code Contest (IOCCC, Международный конкурс на самый запутанный код) выбирают лучшие образцы кода из тех, которые работают совсем не так, как должны на первый взгляд. Некоторые программисты создают настоящие произведения ASCII-искусства – даже в голову не придет, что это программа, которая корректно компилируется.

Взгляните на рисунок. На вид это просто портрет, а на самом деле – исходный код; запустите его, и он выведет на экран другую картинку. На сайте конкурса http://www.ioccc.org таких программ много. Мы уверены, что даже настоящие гуру языка C не с ходу в них разберутся. Зато как весело скомпилировать такой пример и посмотреть, что будет...

И это тоже программа

Запустите эту программу – получится… другая картинка.


В заключение

Таковы подлинно экзотические языки программирования. Может, они и не созданы для величия, но все равно волнуют умы. Забавно попробовать Spaghetti, хотя бы для того, чтобы взять да и усложнить простые вещи – это тренирует мозги; а после головоломного Malbolge вы вернетесь к программированию на добром старом C с новым энтузиазмом, как скороход, сбросивший с ноги гирю.

Во врезке Ресурсы вы найдете ссылки на сайты с дополнительной информацией. Испробуйте эти языки сами и обязательно расскажите нам об этом! LXF

Ресурсы

Информация по Spaghetti.

Домашняя страница Befunge.

Come Here.

Углубляемся в Malbolge

… и Whitespace.

  • www.ioccc.org

Конкурс IOCCC.

Разыщите еще что-нибудь сами.

Персональные инструменты
купить
подписаться
Яндекс.Метрика