#macros #common-lisp #ccl
#макросы #common-lisp #ccl
Вопрос:
Я изучил макрос define-easy-handler из пакета hunchentoot (который создает функцию с именем NAME) и заставил часть defun работать, но я не могу заставить этот макрос передавать ИМЯ в список с именем *observers*
:
(defmacro add-observer (name params amp;body body)
;; add NAME to the list *observers*
`(push ,name *observers*)
;; define a lisp function with the name NAME
;; with key arguments given in PARAMS
`(defun ,name (amp;key ,@(loop for p in params
collect p))
,@body)))
Примером вызова #'add-observer
является:
(add-observer below-80 (id blood-sugar)
(when (< blood-sugar 80)
(format t "Patient No: ~A is hypoglycemic." id))
ИМЯ функции определено и работает нормально, но ИМЯ не добавляется в список *observers*
. Не имеет значения, помещаю ли я оба s-выражения в progn или нет. Макрорасширение четко показывает отсутствие вызова push
с и без progn . Что я неправильно интерпретирую?
Редактировать
Когда я пытаюсь это сделать с помощью progn, например:
`(progn
(push ...
(defn ...
он завершается с Unbound variable: below-80
ошибкой. И когда я возвращаю обратные кавычки обратно в #’push и #’defun, снова #’push не работает.
Комментарии:
1. Для меня очень хорошо работает с a
progn
, что вы наблюдаете, когда добавляете это?2. @Madara Uchiha Я добавил, что не так с моим использованием progn. Вероятно, я неправильно использую progn.
3. Если это ваш настоящий макрос, то
(loop for p in params collect p)
это просто(copy-list params)
. Однако копирование здесь кажется ненужным.4. @coredump Спасибо за
copy-list
. Я, как правило, трачу больше времени на написание кода, чем на изучение существующих функций. Я добавилcopy-list
в свой арсенал.5. @coredump О, и просто
(amp;key ,@params)
тоже достаточно. Я думаю, это то, что вы упомянули.
Ответ №1:
Макрос получает исходную форму и вычисляет первую форму результата: (push foo *observers*)
и затем эта форма возвращается в нирвану сборщика мусора, поскольку она нигде не сохраняется, не возвращается ни одному вызывающему, не выполняется, … Это мусор, немедленно. Поэтому умный компилятор может даже удалить его…
Затем форма макроса вычисляет вторую форму (defun ...)
. Эта форма возвращается из макроса, а затем выполняется позже.
Либо вы хотите выполнить первую форму во время развертывания макроса, тогда вам нужно будет сделать ее исполняемой — удалив обратную кавычку и запятую.
Или вы хотите включить его в сгенерированный исходный код, тогда вам нужно вернуть progn
форму, которая включает все вложенные формы.
Вы также можете подумать о последствиях PUSH
vs PUSHNEW
. , когда форма макроса выполняется / расширяется несколько раз…
Нежелательная оценка символов при использовании макросов
Помните: символы являются переменными в коде Lisp. Если вы хотите рассматривать их как символы, как самих себя, тогда вам нужно заключить в кавычки символ или структуру данных, где они встречаются.
Скажем, ваша форма:
(add-observer below-80 ...)
Это означает below-80
, что он не заключен в кавычки.
Теперь вы генерируете код, в котором символ также не заключен в кавычки.:
(push below-80 ...)
Естественно, это пытается оценить переменную below-80
, которая, кажется, не связана в вашем коде.
Если вы хотите below-80
, чтобы во время оценки обрабатывался как символ, вы должны заключить его в кавычки : 'below-80
. Ваш сгенерированный код должен выглядеть следующим образом:
(push 'below-80 ...)
Процитируйте его либо в своем коде
(add-observer 'below-80 ...)
или с помощью макроса в расширение:
(push 'below-80 ...)
Шаблон обратной кавычки для этого
`(progn
(push ',name ...)
...)
Комментарии:
1. Мое намерение — второй случай. Не могли бы вы показать правильное использование progn здесь?
2. @barerd: вам нужно посмотреть на расширение. Если вы не хотите что-то оценивать, вам нужно где-то процитировать это. Либо в коде, либо в расширении макроса.