Макросы
Макросы – это средство, которое позволяет сделать код более эффективным и легким для чтения. Макросы – это мощный рабочий инструмент программирования. Они дают возможность писать компактные, ориентированные на задачу программы, которые автоматически преобразуются в более сложный, но более близкий машине эффективный лисповский код. Синтаксис определения макроса следующий:
(defmacro имя λ-список тело)
Вызов макроса совпадает по форме с вызовом функции, но его вычисление отличается от вычисления вызова функции. Первое отличие состоит в том, что в макросе не вычисляются аргументы. Второе отличие макроса от функции связано с тем, что вычислением тела макроса состоит из двух последовательных этапов. На первом этапе осуществляется вычисление тела определения с аргументами из вызова таким же образом, как и для функции. Этот этап называют этапом расширения или раскрытия макроса. На втором этапе вычисляется полученная из вызова раскрытая форма, значение которой возвращается в качестве значения всего макровызова. Посмотреть код, полученный после этапа расширения макроса, можно используя функцию macroexpand:
(macroexpand макровызов)
Определим макрос setqq, который действует наподобие setq, но блокирует вычисление и второго своего аргумента:
(defmacro setqq (x y)
(list ’setq x (list ’quote y)))
>(setqq L (a b c))
(A B C)
> L
(A B C)
После выполнения этапа макрорасширения получим следующий код:
>(macroexpand ’(setqq L (a b c))
(setq L (quote (A B C)))
Преимущества функций над макросами:
1. Их легче писать.
2. Функции могут быть использованы как функциональные аргументы для отображающих функционалов, а макросы нет.
3. Макросы тяжелее анализировать динамически.
Преимущество макросов над функциями:
1. Функции вычисляют все свои аргументы, а это не всегда желательно. Рассмотрим, например, вызов (setq x 3). Форма setq не может быть функцией, так как функция вычисляет значения всех своих аргументов. Если x не имеет значения, мы не можем присвоить новое значение этой переменной используя setq, если setq является функцией.
2. В Лиспе все параметры функций передаются по значению. Это приводит к трудностям в изменении значений действительных аргументов.
3. Вызов функции может быть дорогой операцией, особенно посреди длинной итерации. При использовании мелких функций время уходящее на подготовку вызова превышает время выполнения самой функции. Но зачастую такие мелкие функции желательно использовать для читабельности и элегантности кода. Эту проблему можно решить с помощью макросов. Этап макрорасширения выполняется во время компиляции и, таким образом, код выполняется также быстро, как если бы он был написан inline.
4. Макросы позволяют писать более компактный, элегантный код, который часто легче анализировать статически.
5. Макросы могут предоставлять экстра-уровень абстракции.
Обратная блокировка
При раскрытии макроса обычно используется большое количество вложенных друг в друга вызовов функций CONS, CDR, LIST, APPEND и др. Поэтому при построении расширения можно легко ошибиться. Для облегчения написания макросов в Лиспе принят специальный механизм блокировки вычислений, который называется обратной блокировкой и помечается обратным апострофом – «`»(back quote). Внутри обратно блокированного выражения можно по желанию локально отменять блокировку вычислений. Отмена блокировки помечается запятой «,» перед каждым предназначенным для вычисления подвыражением.
Например:
> ’(1 (+ 3 4) 2)
(1 (+ 3 4) 2)
> ‘(1 ,(+ 3 4) 2)
(1 7 2)
Знак «,» может использоваться для вычисления значения любой формы, а не только переменной:
>(setf x ’(ORANGES APPLES))
(ORANGES APPLES)
>‘(I like ,(first x) but not ,(second x))
(I LIKE ORANGES BUT NOT APPLES)
В расширяемом выражении при обратной блокировке выражения с предваряющей запятой заменяются на их значения. Такую форму отмены блокировки называют замещающей. Комбинация символов запятая и знака @ перед выражением означает, что выражение вычисляется обычным образом, но полученное значение присоединяется к конечному выражению таким же образом, как это делает функция append. Такую форму отмены блокировки называют присоединяющей.
Например:
>(setq x ’(2 3))
(2 3)
>‘(1 ,х 4)
(1 (2 3) 4)
>(1 ,@x 4)
(1 2 3 4)
Обратная блокировка является удобным средством формирования макроса. С ее помощью макрос можно определить в форме, близкой к виду его раскрытого выражения. Так, например, определенный выше макрос setqq можно записать используя обратную блокировку следующим образом:
(defmacro setqq (x y)
‘(setq ,x (quote ,y)))