Макросы

Написание функций это один из способов расширения возможностей языка. Однако, это не единственная возможность. Язык lisp представляет ещё одну возможность под названием макросы. Это не макросы языка си, это более мощная вещь. Сейчас я о ней расскажу.

При обработке lisp программ код выполняется не только при запуске, но и при компиляции. Таким образом, можно написать программу, которая дописывает себя во время сборки.

Квазицитирование

Синтаксис квазицитирования Эквивалентный создающий списки код Результат
`(a (+ 1 2) c) (list ‘a ‘(+ 1 2) ‘c) (a (+ 1 2) c)
`(a ,(+ 1 2) c) (list ‘a (+ 1 2) ‘c) (a 3 c)
`(a (list 1 2) c) (list ‘a ‘(list 1 2) ‘c) (a (list 1 2) c)
`(a ,(list 1 2) c) (list ‘a (list 1 2) ‘c) (a (1 2) c)
`(a ,@(list 1 2) c) (append (list ‘a) (list 1 2) (list ‘c)) (a 1 2 c)

Тут описан принцип квазицитирования, и сейчас он нам пригодится для описания макросов.

Создание макросов

(defmacro name(args) body)

Вот так просто описываются макросы. А теперь примеры

(defmacro do-primes ((var start end) &body body)
    `(do ((,var (next-prime ,start) (next-prime (1+ ,var))))
        ((> ,var ,end))
    ,@body))

Этот макрос (я безбожно содрал с PCL как и табличку) проходит циклом по всем простым числам от start до end. Для того, чтобы расписать макрос. Другими словами увидеть во что он раскроется есть функция macroexpand-1

(macroexpand-1 '(do-primes (i 1 5) (print i)))

DO ((I (NEXT-PRIME 1) (NEXT-PRIME (1+ I)))) ((> I 5)) (PRINT I))

Видно, что макрос раскрылся в цикл. NEXT-PRIME не объявлено, так что работать он не будет. Это ещё одна особенность макроса — нельзя быть уверенным работает он или нет, поскольку код в нём не проверен пока не запустится.

Есть ещё пара полезных функций для макросов. Это getsym — возвращает уникальное имя для переменных. Для того, чтобы макрос не перекрыл какую-либо переменную и with-gensyms — для автоматического задания нескольких переменных.

Макросы циклы

Всего в lisp 4 вида циклов (наш do-primes стал 5-м) и все они являются макросами над операцией goto. Один общий цикл — do и его варианты по-проще dotimes, dist:

(dotimes (i 4) (print i))
(dolist (el (list 1 2 3 4)) (print el))
(do (vars*)
    (end-cond result-form)
    body)

Макрос do довольно перегружен скобками, особенно если всё указывать и переменные, и условие, и формы. Поэтому здесь я привожу его в облегчённой форме. Кроме этих циклов есть ещё цикл loop, но про него попозже, это отдельная тема на одну статью.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.