在我对 Clojure For Comprehension example 的回答中,我想删除一些重复内容:
(def all-letters (map char (range 65 90)))
(defn kw [& args] (keyword (apply str args)))
(concat
(for [l all-letters] (kw l))
(for [l all-letters l2 all-letters] (kw l l2))
(for [l all-letters l2 all-letters l3 all-letters] (kw l l2 l3)))
我想删除重复的“for”。我编写了以下宏:
(defmacro combine [times]
(let [symbols (repeatedly times gensym)
for-params (vec (interleave symbols (repeat 'all-letters)))]
`(for ~for-params (kw ~@symbols))))
适用于:
(concat (combine 1) (combine 2) (combine 3))
但是如果我尝试:
(for [i (range 1 4)] (combine i))
我得到:
CompilerException java.lang.ClassCastException: clojure.lang.Symbol cannot be cast to java.lang.Number, compiling:(NO_SOURCE_PATH:177)
这是怎么回事?有没有更好的方法来删除重复项?
最佳答案
您的问题是 combine
是一个在编译时扩展的宏。当您尝试扩展符号 i
时,它会失败,因为它被设计为采用数字(次)。 i
只是编译时的一个符号,它仅在运行时计算为数值。
我建议将 combine
重写为函数而不是宏:这里实际上并不需要宏,并且函数通常更方便(如本例所示!)。
这是一个递归组合,可能大致可以满足您的要求:
(defn combine
([times] (combine times nil))
([times letters]
(if (<= times 0)
(keyword (apply str letters))
(flatten (for [l all-letters] (combine (dec times) (cons l letters)))))))
关于macros - 减少 "for"理解重复,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11532058/