繁体   English   中英

Clojure宏无法与Doseq一起正常工作

[英]clojure macro not working properly with doseq

更新:谢谢大家的回答,但是在我的示例中,似乎使用嵌入式“ 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或调用,因此使用剂量q时它实际上只驱动一次。 如果是这种情况,那么您将如何遍历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。因此,编译器静态插入此表达式,并忽略运行时版本,这就是为什么我每次都看到相同的值。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM