Common lisp 在代码编译/求值期间扩展嵌套宏调用
我有下一段代码:Common lisp 在代码编译/求值期间扩展嵌套宏调用,common-lisp,Common Lisp,我有下一段代码: (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 "
(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))
通用Lisp实现:“SBCL 1.3.16”
(编译文件“source.lisp”)
的结果:
; 编译(包内:CL-USER)
; 编译(DEFMACRO TEST0…)
; 编译(DEFMACRO TEST1…)test0:扩展阶段:FORMtest0:扩展阶段:FORM
(加载“source.lisp”)
的结果:
; #
; 测试0
test0:扩展阶段:表单
; 测试1
(test0表单)
在test1
宏的定义中展开和处理?为什么不在宏调用中处理它呢编译文件
)只打印两次(加载
),而在求值期间只打印一次(加载
)defmacro test1
被视为函数的定义,因此所有代码都会得到适当的处理,并且当SBCL编译defmacro test1
时,(test0表单)
会进行宏扩展。这回答了问题1
“按顺序执行
它遇到的每一种形式”,所以要回答你的问题2,你需要阅读,特别是,
至于宏的展开次数,这不是由
标准(一个实现可以在用户每次调用时扩展它)
函数!),这就是为什么它对于宏不是一个好主意的原因
产生副作用(如输出)。
在您的情况下,当定义了test1
时,加载需要扩展。
编译在定义test1
时扩展,然后在
编辑它。
请记住,defmacro
安排宏可以在
必须立即定义以下代码(包括其本身的递归),so is。宏是对代码进行操作的函数。
因为在Lisp中,代码只是一个
,
看起来像普通函数
您的defmacro test1
被视为函数的定义,因此所有代码都会得到适当的处理,并且当SBCL编译defmacro test1
时,(test0表单)
会进行宏扩展。这回答了问题1
“按顺序执行
它遇到的每一种形式”,所以要回答你的问题2,你需要阅读,特别是,
至于宏的展开次数,这不是由
标准(一个实现可以在用户每次调用时扩展它)
函数!),这就是为什么它对于宏不是一个好主意的原因
产生副作用(如输出)。
在您的情况下,当定义了test1
时,加载需要扩展。
编译在定义test1
时扩展,然后在
编辑它。
请记住,defmacro
安排宏可以在
下面的代码(包括其本身的递归)必须立即定义so is。感谢您的回复,这对我帮助很大。但我仍不清楚一些要点。“按顺序执行遇到的每个窗体”-对于宏窗体,它表示“展开并计算结果”。但是函数形式是如何执行的呢?我的意思是,函数在调用时对子窗体(body)求值。但是在
加载
或编译文件
过程中会做什么?据我所知,编译器可以以某种方式优化调用,例如,内联函数。但是,在标准中,函数形式是如何执行的?我建议您单独问一个关于“顶级形式”的问题。评论不是回答新问题的好地方。谢谢你的回答,这对我很有帮助。但我仍不清楚一些要点。“按顺序执行遇到的每个窗体”-对于宏窗体,它表示“展开并计算结果”。但是函数形式是如何执行的呢?我的意思是,函数在调用时对子窗体(body)求值。但是在加载
或编译文件
过程中会做什么?据我所知,编译器可以以某种方式优化调用,例如,内联函数。但是,在标准中,函数形式是如何执行的?我建议您单独问一个关于“顶级形式”的问题。评论不是提出新问题的合适地方。