2

我研究了 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 未添加到 list *observers*。我是否将两个 s 表达式都放在一个 progn 中并不重要。macroexpand 清楚地显示了在push有和没有 progn 的情况下都没有调用。我在解释什么错误?

编辑

当我用这样的progn尝试这个时:

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

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

4

1 回答 1

3

宏获取源形式并计算第一个结果形式:(push foo *observers*)然后将该形式返回到垃圾收集器的必杀技中,因为它没有存储在任何地方,没有返回给任何调用者,没有执行,......它是垃圾,立即. 所以一个聪明的编译器甚至可能会删除它......

然后宏形式计算第二种形式(defun ...)。此表单从宏返回,然后执行。

您想在宏扩展时执行第一个表单,然后您需要使其可执行 - 通过删除反引号和逗号。

或者你想将它包含到生成的源代码中,那么你需要返回一个progn表单,它包含了所有的子表单。

您可能还想考虑PUSHvs.的影响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 ...)
   ...)
于 2016-10-04T22:28:46.123 回答