为什么这个常见的lisp宏不计算第一个s-exp



我从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表单,它包含了所有的子表单。

您可能还想考虑PUSHPUSHNEW的影响,当宏表单被执行/展开多次时…

使用宏时不需要的符号求值

记住:符号在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 ...)
   ...)

最新更新