2

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

何が起こっている?重複を取り除くためのより良い方法はありますか?

4

2 に答える 2

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)))))))
于 2012-07-17T23:13:02.400 に答える
1

concat以下に示すように、がマクロの一部になるようにマクロを変更できます。しかし、私はミケラに、機能を持っている方が良いということに同意します。

(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)
于 2012-07-18T04:28:27.093 に答える