Macros 减少;至于;理解重复

Macros 减少;至于;理解重复,macros,clojure,code-duplication,for-comprehension,Macros,Clojure,Code Duplication,For Comprehension,在我对的答复中,我想消除一些重复: (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

在我对的答复中,我想消除一些重复:

(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)))))))
(defn联合收割机
([次数](合并次数为零))
([泰晤士报]

(如果(您可以修改宏,使
concat
成为宏的一部分,如下所示。但我同意Mikera的观点,最好有一个函数

(defmacro combine [start end]
 `(concat
      ~@(for [i (range start end)]
          (let [symbols (repeatedly i gensym)
             for-params (vec (interleave symbols (repeat 'all-letters)))]
          `(for ~for-params (kw ~@symbols))))))

user=> (combine 1 2)
(:A :B :C :D :E :F :G :H :I :J :K :L :M :N :O :P :Q :R :S :T :U :V :W :X :Y)

这是因为“for”也是一个宏吗?
for
不是导致问题的原因,它是一个宏,但它在运行时会产生预期的效果-真正的问题是
i
,它是编译时在
combine
宏中使用的一个符号。添加了一些解释性说明。