为什么这个常见的 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
表单,它包含了所有的子表单。
您可能还想考虑 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 ...)
...)
我研究了 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
表单,它包含了所有的子表单。
您可能还想考虑 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 ...)
...)