Все, что делает макрос - это принимает в качестве параметров неоцененные формы и выполняет замену его тела.Хитрость в реализации макросистемы состоит в том, чтобы сказать вашему компилятору lazy .
Другими словами, когда компилятор встречает функцию, он сначала оценивает свой список формальных параметров, выдаетрезультаты и передает их в функцию.Когда компилятор находит макрос, он передает аргументы неоцененные в тело, затем выполняет любые вычисления, которые запрашивает тело, и, наконец, заменяет собой результат этих операций.
Например, давайтескажем, у вас есть функция:
(defun print-3-f (x) (progn (princ x) (princ x) (princ x)))
и макрос:
(defmacro print-3-m (x) `(progn (princ ,x) (princ ,x) (princ ,x)))
Тогда вы сразу увидите разницу:
CL-USER> (print-3-f (rand))
* 234
* 234
* 234
CL-USER> (print-3-m (rand))
* 24
* 642
* 85
Чтобы понять, почему этоТо есть вам, если можно так выразиться, нужно запустить компилятор в своей голове.
Когда Лисп сталкивается с функцией, он создает дерево, в котором (rand)
сначала оценивается и результат передается вфункция, которая печатает указанный результат три раза.
С другой стороны, когда Лисп встречает макрос, он передает форму (rand)
нетронутой в тело, которое возвращает список в кавычкахгде x
заменяется на (rand)
, давая:
(progn (princ (rand)) (princ (rand)) (princ (rand)))
и заменяя вызов макроса для этой новой формы.
Здесь вы найдете огромное количестводокументации относительно макросовна разных языках, включая Лисп.