[英]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.