2012-07-17 27 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)