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 "test1: Expansion phase: ~s" ,form))
常见的 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代码中只是一个
list
,
宏函数看起来像普通函数。
您defmacro test1
被视为函数的定义,因此所有代码都得到适当处理,并(test0 form)
在 SBCL 编译时进行宏扩展defmacro test1
。这回答了 q1。
load
“依次执行它遇到的每个表单”,因此要回答您的 q2,您需要阅读3.1 Evaluation,具体来说,
3.1.2.1.2.2 Macro Forms。
至于宏被扩展多少次,标准没有规定(实现可以在每次调用用户函数时扩展它!),这就是为什么宏不是一个好主意的原因副作用(例如,做输出)。在您的情况下,加载需要在test1
定义时进行扩展。编译在定义时扩展test1
,然后在编译时再次扩展。请记住,defmacro
安排宏可以在以下代码中使用(包括递归地),因此必须立即定义。
推荐阅读
- kotlin - 限制 Kotlin 中伴随对象的类型
- python-3.x - 对不同的滚动窗口应用 lambda 函数
- android - ##[错误]错误:进程'/Library/Frameworks/Mono.framework/Versions/5_18_2/bin/msbuild'失败,退出代码为1
- asp.net-core - 如何使用 csharp 修改函数 GetSelectStatement 以生成内部连接选择语句?
- android - 如何在 QT 中永久保存变量值?
- batch-file - 关闭所有其他命令提示符窗口并启动一个新窗口
- android - 通过 ViewModel 在 Fragment 之间传递数据
- java - Continuos 数据源崩溃
- javascript - 如何使用附加的参考重新创建相同的突出显示效果?
- c# - 如何在异步 HttpClient 调用中捕获所有异常?