我从hunchentoot包中学习了define-easy-handler宏(它创建了一个名为name的函数),并使defun部分工作,但我无法让这个宏将name推入名为*observers*
的列表:
(defmacro add-observer (name params &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 (&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))
定义了函数NAME并且工作正常,但是没有将NAME添加到*observers*
列表中。我是否把两个s表达式都放在一个程序中并不重要。宏展开清楚地显示,无论是否使用程序,都没有对push
的调用。我理解错了什么?
当我用这样的程序尝试时:
`(progn
(push ...
(defn ...
在Unbound variable: below-80
中失败。当我把反引号放回#'push和#'defun时,#'push还是不起作用
宏获得源形式并计算第一个结果形式:(push foo *observers*)
,然后该形式返回到垃圾收集器的涅盘,因为它不存储在任何地方,不返回给任何调用者,不执行,…马上就是垃圾。所以一个聪明的编译器甚至可以删除它…
(defun ...)
。此表单从宏返回,然后执行。
如果您想在宏展开时执行第一种形式,那么您需要使其可执行-通过删除反引号和逗号。
或者您想要将它包含到生成的源代码中,那么您需要返回一个progn
表单,它包含了所有的子表单。
您可能还想考虑PUSH
与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 ...)
...)