更新:感谢大家的回复,但我似乎在我的例子中使用嵌入式“def”做了一个糟糕的选择,这让人们失望。这与def无关。如果我不使用def,问题仍然存在。至于为什么我这样做 - 老实说,我只是想了解宏,这只是我遇到的方式之一。我只是想了解宏是如何工作的。我最终可能最终会使用不同的机制。我也知道对同一件事进行多次defs(包括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次迭代是我的最终解决方案,所以我真的不想要在线所有定义。
答案 0 :(得分:0)
我很确定我知道发生了什么。这是一个经典的宏观事物 - 区分编译时和运行时。
我认为在编译do-seq时,编译器需要将某些内容放入'(xyz)表达式中:
(spit "log.txt" (format "pass %s: result=%s\n" i (xyz)) <-- Compiler: what do I put here?....
我假设它将来自先前defmacro设置的'xyz的运行时值:
(defmacro abc []
`(defmacro xyz [] <-- I'm assuming this version will be used
(+ 9 1)))
(abc)
虽然在do-seq的编译时中已知'abc,但'abc defmacro中的基础宏'xyz仅在运行时中已知。编译器只知道我之前运行的'xyz符号集,它返回10.因此编译器静态插入此表达式,并忽略运行时版本,这就是我每次都看到相同值的原因。