2012-07-17 49 views
2

在我的回答对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))) 

我想删除了“”重复。我写了下面的宏:

(defmacro combine [times] 
(let [symbols (repeatedly times gensym) 
     for-params (vec (interleave symbols (repeat 'all-letters)))] 
    `(for ~for-params (kw [email protected])))) 

与工程:

(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) 

这是怎么回事上?有没有更好的方法来消除重复?

回答

1

你的问题是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))))))) 
+0

这是因为“for”也是一个宏吗? – DanLebrero 2012-07-17 23:17:26

+0

'for'不是造成这个问题的原因,它是一个宏,但它具有你在运行时期望的效果 - 真正的问题是'i',它是编译时用于'combine宏。增加了一些更多的解释性说明。 – mikera 2012-07-17 23:25:20

1

您可以修改您的宏使得concat成为宏的一部分,如下面所示。但我同意Mikera认为最好有一个功能。

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

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)