【发布时间】:2015-03-27 09:48:31
【问题描述】:
更新:感谢大家的回复,但在我的示例中使用嵌入式“def”似乎做出了错误的选择,这让人们望而却步。这与def无关。如果我不使用def,问题仍然存在。至于我为什么这样做——老实说,我只是想了解宏,这只是我想到的一种方式。我只是想了解宏是如何工作的。我很可能最终会使用不同的机制。我也知道为同一事物设置多个 def(包括 defmacros)被认为是不好的做法,但在我看来,这种方式仍然应该有效。
我正在重构我的示例:
当我在线编写宏生成宏的变体时(使用我实际操作的简化版本):
(do
(defmacro abc []
`(defmacro xyz []
;;(def x 7)))
(+ 7 1)))
(abc)
;;(xyz)
;;(spit "log.txt" (format "pass 1: x=%s\n" x ) :append false))
(spit "log.txt" (format "pass 1: results=%s\n" (xyz) ) :append false))
(do
(defmacro abc []
`(defmacro xyz []
;;(def x 8)))
(+ 8 1)))
(abc)
;;(xyz)
;;(spit "log.txt" (format "pass 2: x=%s\n" x ) :append true))
(spit "log.txt" (format "pass 1: results=%s\n" (xyz) ) :append false))
(do
(defmacro abc []
`(defmacro xyz []
;;(def x 9)))
(+ 9 1)))
(abc)
;;(xyz)
;;(spit "log.txt" (format "pass 3: x=%s\n" x ) :append true))
(spit "log.txt" (format "pass 1: results=%s\n" (xyz) ) :append false))
它给了我我的期望:
pre-refactor:
cat log.txt
pass 1: x=7
pass 2: x=8
pass 3: x=9
post-refactor:
cat log.txt
pass 1: results=8
pass 2: result=9
pass 3: result=10
但是当我尝试使用doseq进行迭代时,它似乎只给了我一个值:
(def int-lookup [7 8 9])
(doseq [i (range 3)]
(defmacro abc []
`(defmacro xyz []
;;(def x ~(int-lookup i))))
(+ 1 ~(int-lookup i))))
(abc)
;;(xyz)
;;(spit "log.txt" (format "pass %s: x=%s\n" i x) :append (if (= i 0) false true)))
(spit "log.txt" (format "pass %s: result=%s\n" i (xyz)) :append (if (= i 0) false true))
输出:
pre-refactor:
cat log.txt
pass 0: x=9
pass 1: x=9
pass 2: x=9
post-refactor
cat log.txt
pass 0: result=10
pass 1: result=10
pass 2: result=10
我见过它给了我所有的 7 分,也给了我所有的 8 分,但从来没有混合过。
我尝试过像这样重置中间的宏符号:
(ns-unmap *ns* 'xyz)
(ns-unmap *ns* 'x)
然而,这会让事情变得更糟,偶尔会产生:
CompilerException java.lang.RuntimeException: Unable to resolve symbol: xyz in this context, compiling:(/tmp/form-init2424586203535482807.clj:5:5)
我有点假设编译器正在以某种方式优化宏 def 或调用,所以它实际上只在使用 doseq 时驱动它一次。如果是这种情况,那么您将如何迭代 defmacro 定义而不发生这种情况?我打算有大约 15 次迭代是我的最终解决方案,所以我真的不想内联所有定义。
【问题讨论】:
-
为什么需要这么多嵌套宏?
-
为什么要首先重新定义宏或变量,嵌套宏是否有原因?这对我来说看起来很复杂。也根本不需要
abc宏,您也可以直接在其中使用(defmacro xyz,除非您的实际代码需要它。 -
更详细一点:defmacro 的工作方式与defn 很像,而
def又使用def。def创建一个 var,实习它并绑定它的根值,参见。 http://clojure.org/vars。您似乎在xyz宏中使用def主要是为了重新绑定x。如果您不需要在所有线程中设置根值,您可以为此使用set!。我认为无法保证所有这些重新绑定会发生什么。 -
问题不在于嵌套宏,而在于其中包含 defmacro 的测试。
defs 应该只出现在顶层(或顶层做)。 -
如何在不使用 defmacro 的情况下定义嵌入式/嵌套宏? Paul Graham 在“On Lisp”中有一整章专门介绍宏编写宏。引自第 213 页:“当多个宏具有相似形式的定义时,我们可以编写一个宏定义宏来生成它们”。在他的示例中,他在 'defmacros 中有 'defmacros,所以我认为你应该能够在 clojure 中做同样的事情