我有下一段代码:
(in-package :cl-user)
(defmacro test0 (form)
(format t "test0: Expander phase: ~s" form)
`(format t "test0: Expansion phase: ~s" ,form))
(defmacro test1 (form)
(format t "test1: Expander phase: ~s" form)
(test0 form)
`(format t "test1: Expansion phase: ~s" ,form))
Run Code Online (Sandbox Code Playgroud)
Common Lisp实现:"SBCL 1.3.16".
结果(compile-file "source.lisp"):
; compiling (IN-PACKAGE :CL-USER)
; compiling (DEFMACRO TEST0 ...)
; compiling (DEFMACRO TEST1 ...)test0: Expander phase: FORMtest0: Expander phase: FORM
结果(load "source.lisp"):
; #<PACKAGE "COMMON-LISP-USER">
; TEST0
test0: Expander phase: FORM
; TEST1
而我无法理解下一件事:
为什么(test0 form)在test1宏的定义中扩展和处理嵌套子表单?为什么不在宏调用中处理它呢?
Common Lisp标准在哪里指定这样的行为?eval-when?文件编译?表格评估?
最后,为什么"test0: Expander phase: FORM"在编译期间打印两次,compile-file在评估期间只打印一次(load)?
我认为答案非常明显,但我找不到它们.
甲宏是上操作的功能码.由于在Lisp 代码中只是a
list,
宏函数看起来像普通函数.
您defmacro test1被视为函数的定义,因此所有代码都经过适当处理,并(test0 form)在SBCL编译时进行宏扩展defmacro test1.这回答了q1.
load"按顺序执行它遇到的每个表单",所以要回答你的q2,你需要阅读3.1评估,特别是
3.1.2.1.2.2宏表格.
至于扩展宏的次数,标准没有指定(实现可以在每次调用用户函数时扩展它!),这就是为什么宏不是一个好主意的原因.副作用(例如,做输出).在您的情况下,加载需要在test1定义时进行扩展.编译在定义时会扩展test1,然后在编译时再次展开.请记住,defmacro安排宏可以在以下代码中使用(包括本身递归),因此必须立即定义.