clojure宏与doseq无法正常工作

时间:2015-03-27 09:48:31

标签: clojure macros

更新:感谢大家的回复,但我似乎在我的例子中使用嵌入式“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次迭代是我的最终解决方案,所以我真的不想要在线所有定义。

1 个答案:

答案 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.因此编译器静态插入此表达式,并忽略运行时版本,这就是我每次都看到相同值的原因。