Clojure中的macroexpand和macroexpand-1有什么区别

时间:2016-05-05 10:26:51

标签: clojure functional-programming macros lisp

我无法理解macroexpand和macroexpand-1之间的区别。

你能提供一些例子吗?

2 个答案:

答案 0 :(得分:11)

假设我们有以下代码:

(defmacro inner-macro [arg]
  `(println ~arg))

(defmacro top-level-macro [arg]
  `(inner-macro ~arg))

(defn not-a-macro [] nil)

然后,macroexpand-1的文件说:

  

如果form表示宏表单,则返回其扩展,   否则返回表格。

确实如此:

user> (macroexpand-1 '(inner-macro "hello"))
(clojure.core/println "hello")

user> (macroexpand-1 '(top-level-macro "hello"))
(user/inner-macro "hello")

user> (macroexpand-1 '(not-a-macro))
(not-a-macro)

换句话说,如果提供的表单是宏表单,macroexpand-1只执行宏展开的一步。

然后,macroexpand的文件:

  

在窗体上反复调用macroexpand-1,直到它不再存在   代表一个宏形式,然后返回它。

示例:

user> (macroexpand '(top-level-macro "hello"))
(clojure.core/println "hello")

发生什么事了?只要(top-level-macro "hello")扩展为(user/inner-macro "hello"),这是宏格式,macroexpand将再次执行扩展。第二次扩展的结果是(clojure.core/println "hello")。它不是宏形式,因此macroexpand只返回它。

因此,只需重新定义文档,macroexpand将递归执行扩展,直到 顶级 表单不是宏格式。

macroexpand的文档中还有其他说明:

  

请注意,macroexpand-1和macroexpand都不会在子表单中展开宏。

这是什么意思?假设我们还有一个宏:

(defmacro subform-macro [arg]
  `(do
     (inner-macro ~arg)))

让我们尝试扩展它:

user> (macroexpand-1 '(subform-macro "hello"))
(do (user/inner-macro "hello"))

user> (macroexpand '(subform-macro "hello"))
(do (user/inner-macro "hello"))

由于(do ...)表单不是宏macroexpand-1,而macroexpand只是返回它而已。不要指望macroexpand会执行以下操作:

user> (macroexpand '(subform-macro "hello"))
(do (clojure.core/println "hello"))

答案 1 :(得分:4)

差异很简单。首先是背景:当编译器看到宏调用时,它会尝试根据其定义扩展它。如果由此宏生成的代码包含其他宏,则它们也会被编译器扩展,依此类推,直到生成的代码完全无宏。所以macroexpand-1只是扩展最顶层的宏并显示结果(无论它是否生成另一个宏调用),而macroexpand尝试遵循编译器的管道(部分,不扩展子模式中的宏)要进行完整扩展,您应该查看clojure.walk/maxroexpand-all)。

小例子:

user> (defmacro dummy [& body]
        `(-> ~@body))
#'user/dummy

这个愚蠢的宏生成对另一个宏(->

的调用
user> (macroexpand-1 '(dummy 1 (+ 1)))
(clojure.core/-> 1 (+ 1))

macroexpand-1只展开dummy,但保持->未展开

user> (macroexpand '(dummy 1 (+ 1)))
(+ 1 1)

macroexpand展开dummy,然后展开->