为什么这个常见的 lisp 宏不评估第一个 s-exp?

Why does this common lisp macro not evaluate the first s-exp?

我研究了 define-easy-handler macro from the hunchentoot package(它创建了一个名为 NAME 的函数),并使 defun 部分起作用,但我无法让这个宏将 NAME 推送到名为 [=13= 的列表]:

(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 表达式都放在 progn 中并不重要。 macroexpand 清楚地显示了在有和没有 progn 的情况下不存在对 push 的调用。我解释错了什么?

编辑

当我用这样的程序尝试这个时:

`(progn
  (push ...
  (defn ...

失败 Unbound variable: below-80。当我将反引号放回#'push 和#'defun 时,#'push 再次不起作用。

宏获取源形式并计算第一个结果形式:(push foo *observers*)然后这个形式被return编入垃圾收集器的必杀技,因为它没有存储在任何地方,不returned to any caller, not executed, ...立即成为垃圾。所以聪明的编译器甚至可能会删除它...

宏形式然后计算第二个形式(defun ...)。此表单是 return 从宏编辑而来,然后执行。

如果您想在宏展开时执行第一个表单,那么您需要使其可执行 - 通过删除反引号和逗号。

或者你想把它包含到生成的源代码中,那么你需要return一个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 ...)
   ...)