clojure宏与doseq不能正常工作

clojure宏与doseq不能正常工作,clojure,macros,Clojure,Macros,更新:感谢大家的回复,但在我的示例中,使用嵌入式def似乎是一个错误的选择,这让人感到厌烦。这与def无关。如果我不使用def,问题仍然会发生。至于我为什么这样做——老实说,我只是想学习宏,这只是我想到的方法之一。我只是想了解宏是如何工作的。我很可能最终会使用一种不同的机制。我也知道,为同一件事使用多个def(包括defmacros)被认为是一种不好的做法,但在我看来,这种方式仍然应该有效 我正在重新考虑我的例子: 当我编写宏的变体时,生成的宏与我实际操作的简化版本一致: (do (d

更新:感谢大家的回复,但在我的示例中,使用嵌入式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次迭代,这是我的最终解决方案,所以我真的不想把所有定义都串联起来。

我很确定我知道发生了什么。这是一个经典的宏操作——区分编译时和运行时

我认为在编译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)

虽然“abc”在do seq的编译时已知,但“abc defmacro”中的基础宏“xyz”仅在运行时已知。编译器只知道我上次运行的“xyz”符号集,返回10。因此,编译器静态地插入此表达式,并忽略运行时版本,这就是为什么我每次都看到相同的值。

为什么需要这么多嵌套宏?为什么首先要重新定义宏或变量?是否有理由使用嵌套宏?我觉得这很复杂。也完全不需要abc宏,您也可以直接将defmacro xyz放在那里,除非您的实际代码需要它。def创建一个var,将其插入并绑定其根值,cf。您似乎在xyz宏中使用def主要用于重新绑定x。你可以用一套!为此,如果不需要跨所有线程设置根值。我认为不能保证所有这些重新绑定会发生什么。嵌套宏的问题并不比测试中包含defmacro的问题多。DEF只能在顶级或顶级do中出现。如果不使用defmacro,如何定义嵌入/嵌套宏?Paul Graham在Lisp中有一整章专门讨论宏编写宏。引用第213页:当几个宏具有类似形式的定义时,我们可以编写一个宏定义宏来生成它们。他的示例中有“defmacros in”defmacros,所以我假设您在clojure中也可以这样做