【问题标题】:What is the difference between macroexpand and macroexpand-1 in ClojureClojure中的macroexpand和macroexpand-1有什么区别
【发布时间】:2016-08-31 02:21:00
【问题描述】:

我不明白macroexpand 和macroexpand-1 之间的区别。

你能提供例子吗?

【问题讨论】:

    标签: clojure functional-programming macros lisp


    【解决方案1】:

    假设我们有以下代码:

    (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-1macroexpand 只需返回它即可。不要指望macroexpand 会做以下事情:

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

    【讨论】:

      【解决方案2】:

      区别很简单。首先是背景:当编译器看到宏调用时,它会尝试根据其定义对其进行扩展。如果此宏生成的代码包含其他宏,它们也会被编译器扩展,依此类推,直到生成的代码完全没有宏。所以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 然后扩展 ->

      【讨论】:

        猜你喜欢
        • 2022-01-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-08-25
        • 2016-12-17
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多