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
    宏的定义中展开和处理?为什么不在宏调用中处理它呢

  • 公共Lisp标准在哪里指定了这种行为<代码>何时评估?文件编译?表格评估

  • 最后,为什么在编译期间(
    编译文件
    )只打印两次(
    加载
    ),而在求值期间只打印一次(
    加载

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

    宏是一种在代码上运行的函数。 因为在Lisp中,代码只是一个 , 看起来像普通函数

    您的
    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)求值。但是在
    加载
    编译文件
    过程中会做什么?据我所知,编译器可以以某种方式优化调用,例如,内联函数。但是,在标准中,函数形式是如何执行的?我建议您单独问一个关于“顶级形式”的问题。评论不是提出新问题的合适地方。