Статья посвящена изучению базиса языка Лисп.
Автор:
Написал: artish   Дата: 2008-09-08 21:16
Комментарии: (0)   Рейтинг:
Пользовательская оценка (от 1 до 10): 7.50   
Проголосовавших: 2 с 2009-05-06 20:23

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

А у Лиспа синтаксис очень простой. В первом приближении, текст программы состоит из круглых скобок, пробелов и идентификаторов. Соответственно, идентификаторы разделяются пробелами или скобками. Зарезервированных идентификаторов нет. Любой идентификатор рассматривается как функция или макрос, если он стоит после открывающей скобки, в противном случае, он рассматривается как переменная. В Лиспе нет ни операций, ни операторов. Файл исходного текста - это набор вызовов функций или макросов.

Идентификаторы могуть включать в себя почти любые character-ы.

Вместо f(a,b) пишется (f a b)
соответственно, 2+2 это (+ 2 2)
if a then b else c - это (if a b c)

определить функцию - это
(defun имяФункции списокПараметров тело)

тело - это несколько вызовов других функций. Возвращается результат последнего вычисления в теле. Параметры передаются по ссылке, кроме чисел.
(defun plus (x y) (+ x y))

присвоить что-то - это
(setf *m* 2)
в переменную *m* мы записали 2. Если такой переменной нет, то она создается и становится глобальной. По традиции, глобальные имена рекомендуется начинать со звездочки и заканчивать звездочкой. Теперь

(* 2 *m*) вернет 4

В setf можно присваивать значение не только переменной, но и другим "местам", подобным lvalue в C. Например, можно присвоить
(setf (gethash ключ хеш-таблица) значение-для-ключа)

определить локальные переменные - это делается так.
(let ((имя1 значение1) (имя2 значение2)) тело)
- переменные определены внутри тела. Локальные переменные действуют в плане областей видимости так же, как в Паскале или С.

Еще есть апостроф, который замораживает часть программы, превращяя ее в данные.
(+ 2 2) вернет 4
`(+ 2 2) вернет структуру данных в памяти - список из плюса и двух двоек, который печатается как
как (+ 2 2)

Действие апострофа распространяется до закрывающей скобки (или на один идентификатор, если апостроф стоит перед идентификатором). "Замороженные" данные можно опять превратить в программу, двумя способами: во-первых, можно это сделать с помощью запятой
`( ,(+ 2 3) (+ *m* 8) ,*m*)
- разморозится только по одному элементу после каждой запятой. Получится
(5 (+ *m* 8) 2)

Кстати, t - это "истина". Второй способ - использовать еval. (от evaluate, т.е. "оценить", но на самом деле это будет "выполнить дерево").

Заморозим данные и положим их в таком виде в переменную:
(setf *x* `(+ 2 2))

Теперь просто *x* вернет список
(+ 2 2)

А
(eval *x*)
вычислит *x* (получится список), потом вычислит этот список как выражение и вернет
4

Заморозка и разморозка данных лежат в основе определения макросов. Каждый макрос - это как бы новая структура в языке, и это компенсирует отсутствие операторов. Сейчас мы напишем макрос. В Лиспе, если нужно определить только одну локальную переменную, а не несколько, все равно требуется четыре скобки, которые тут уже - явно лишние. Подсластим эту пилюлю и научим Лисп, как определить одну переменную без лишних скобок:
(defmacro let1 (перем знач &body тело) `(let ((,перем ,знач)) ,@тело))

Перем, знач и тело - это идентификаторы, их можно писать прямо по-русски. Зачем нужна @ после запятой и что такое &body - это уже следующая тема. Главное сейчас - это то, что можно написать
(let1 n 3 (+ n 1))
и произойдет следующее:
1. Макропроцессор вычислит наши полузамороженные данные и получится дерево (let ((n 3)) (+ n 1))
2. Интерпретатор получит не исходную форму, а результат работы макроса. Получится 4.

Макрос может не просто содержать шаблон, в который подставляются параметры макроса, а выполнять ЛЮБЫЕ вычисления: открывать файлы, лазить на сервера, спрашивать пользователя. Обращаться к любым переменным Лисп-системы на чтение и запись. При этом, если текст обрабатывается компилятором, то макросы раскрываются в момент компиляции, что позволяет не тратить на них время в момент исполнения, а также позволяет сквозную проверку и оптимизацию программы. Именно в макросах заключается основа могущества Лиспа. И поэтому Вы до сих пор ничего не узнали из этого текста о функциональном программировании. И даже не узнали о том, что с помощью функции cons можно программно строить любые деревья, а с помощью функций car и cdr - разбирать их.

Еще в Лиспе есть разные обычные типы данных: "строки в двойных кавычках", числа. Например, (expt 2 200), то есть два в двухсотой степени, будет 1606938044258990275541962092341162602522202993782792835301376

Есть массивы, например, одномерные #(1 3 4 5)

Есть структуры (аналоги struct в C или record в Паскале), потоки, хеш-таблицы, классы, специальный тип для хранения путей файлов, битовые маски (для них создан отдельный тип). Короче, есть все, что и в любом нормальном языке. В большинстве реализаций есть связь с C. Стандарта на нее нет, но есть попытки создать слой для переносимости связи с C.

Лисп работает со сборкой мусора (и это не мешает ему иногда работать очень быстро). Типы C могут создаваться и уничтожаться с помощью malloc(), поэтому есть возможность управлять памятью явно.

Вот - базовый минимум Лиспа. По-моему, он очень простой.



Онлайн :

0 пользователь(ей), 8 гость(ей) :




Реклама на сайте: