首页 > 解决方案 > 在代码编译/评估期间扩展嵌套宏调用

问题描述

我有下一段代码:

(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".

  1. 结果(compile-file "source.lisp") ; compiling (IN-PACKAGE :CL-USER) ; compiling (DEFMACRO TEST0 ...) ; compiling (DEFMACRO TEST1 ...)test0: Expander phase: FORMtest0: Expander phase: FORM

  2. 结果(load "source.lisp") ; #<PACKAGE "COMMON-LISP-USER"> ; TEST0 test0: Expander phase: FORM ; TEST1

我只是无法理解接下来的事情:

  1. 为什么在宏(test0 form)的定义中对嵌套子窗体进行扩展和处理?test1为什么不在宏调用中处理它?

  2. Common Lisp 标准在哪里指定这样的行为?eval-when? 文件编译?表格评价?

  3. 最后,为什么"test0: Expander phase: FORM"在编译()期间打印两次,compile-file而在评估(load)期间只打印一次?

我认为答案很明显,但我找不到。

标签: common-lisp

解决方案


是对代码进行操作的函数。由于在 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安排宏可以在以下代码中使用(包括递归地),因此必须立即定义。


推荐阅读