Ert*_*tin 8 lisp macros functional-programming clojure
我无法理解macroexpand和macroexpand-1之间的区别.
你能提供一些例子吗?
Ole*_*Cat 11
假设我们有以下代码:
(defmacro inner-macro [arg]
`(println ~arg))
(defmacro top-level-macro [arg]
`(inner-macro ~arg))
(defn not-a-macro [] nil)
Run Code Online (Sandbox Code Playgroud)
然后,doc 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)
Run Code Online (Sandbox Code Playgroud)
换句话说,macroexpand-1如果提供的形式是宏形式,那么宏扩展只需要一步.
然后,doc macroexpand:
在窗体上重复调用macroexpand-1,直到它不再表示宏窗体,然后返回它.
例:
user> (macroexpand '(top-level-macro "hello"))
(clojure.core/println "hello")
Run Code Online (Sandbox Code Playgroud)
发生了什么?一旦(top-level-macro "hello")扩展(user/inner-macro "hello")为宏形式,macroexpand将再次进行扩展.第二次扩张的结果是(clojure.core/println "hello").它不是宏形式,所以macroexpand只返回它.
因此,只是为了重新定义doc,macroexpand将以递归方式进行扩展,直到顶级表单不是宏形式.
另外还有macroexpand文档中的附加说明:
请注意,macroexpand-1和macroexpand都不会在子窗体中展开宏.
那是什么意思?假设我们还有一个宏:
(defmacro subform-macro [arg]
`(do
(inner-macro ~arg)))
Run Code Online (Sandbox Code Playgroud)
让我们尝试扩展它:
user> (macroexpand-1 '(subform-macro "hello"))
(do (user/inner-macro "hello"))
user> (macroexpand '(subform-macro "hello"))
(do (user/inner-macro "hello"))
Run Code Online (Sandbox Code Playgroud)
因为,(do ...)form不是一个宏macroexpand-1而macroexpand只是返回它而已.不要指望macroexpand会做以下事情:
user> (macroexpand '(subform-macro "hello"))
(do (clojure.core/println "hello"))
Run Code Online (Sandbox Code Playgroud)
区别很简单。首先是背景:当编译器看到宏调用时,它将尝试根据其定义对其进行扩展。如果此宏生成的代码包含其他宏,它们也将由编译器扩展,依此类推,直到生成的代码完全没有宏为止。因此,macroexpand-1仅扩展最上面的宏并显示结果(无论它是否生成另一个宏调用),同时macroexpand尝试遵循编译器的管道(部分地,不要以子形式扩展宏。要进行完整的扩展,您应该看一下clojure.walk/maxroexpand-all)。
小例子:
user> (defmacro dummy [& body]
`(-> ~@body))
#'user/dummy
Run Code Online (Sandbox Code Playgroud)
这个愚蠢的宏会生成对另一个宏(->)的调用
user> (macroexpand-1 '(dummy 1 (+ 1)))
(clojure.core/-> 1 (+ 1))
Run Code Online (Sandbox Code Playgroud)
macroexpand-1只是扩展dummy,但不->扩展
user> (macroexpand '(dummy 1 (+ 1)))
(+ 1 1)
Run Code Online (Sandbox Code Playgroud)
macroexpand扩展dummy然后扩展->