2012-10-10 10 views
6

私が取り組んでいるプロジェクトでは、私が興味深い問題に遭遇しました。私は "The Little Schemer"を読んでいる途中ですので、いくつかの再帰手法を試しています。再帰を使ってこれを行う別の方法があるのか​​、再帰を使わない方法があるのならば興味があるのだろうかと思います。Clojure(または一般的なLisp)でseq - 再帰を分割する

問題は、シーケンスをとり、それをseqのseqに分割することです。例えば、このベクター:

配列を

((:a :d :g) (:b :e :h) (:c :f :i)) 

とではn = 4を生成する、N = 3で分配

[ :a :b :c :d :e :f :g :h :i ] 

((:a :e :i) (:b :f) (:c :g) (:d :h)) 

など。私は2つの機能を使ってこれを解決しました。最初のものは内側のseqsを作成し、もう一方は内側のseqsを作成します。ここに私の機能は以下のとおりです。

(defn subseq-by-nth 
    "Creates a subsequence of coll formed by starting with the kth element and selecting every nth element." 
    [coll k n] 
    (cond (empty? coll) nil 
     (< (count coll) n) (seq (list (first coll))) 
     :else (cons (nth coll k) (subseq-by-nth (drop (+ n k) coll) 0 n)))) 

(defn partition-by-nth 
    "" 
    ([coll n] 
    (partition-by-nth coll n n)) 
    ([coll n i] 
     (cond (empty? coll) nil 
     (= 0 i) nil 
     :else (cons (subseq-by-nth coll 0 n) (partition-by-nth (rest coll) n (dec i)))))) 

私は単純に再帰のために複数のアリティを持つパーティションごとのn番目の機能と完全に満足してないんだけど、別の方法を見ることができませんでした。

これはすべてのテストケースでうまくいくようです。まともなアプローチですか?それはあまりにも複雑ですか?再帰なしでこれを行う方法はありますか、または単一の再帰関数で行うことはできますか?

ありがとうございます。私はClojureとLispの両方に新しいので、私は別のテクニックを拾い上げています。

答えて

9

私はもっとの精神でリトルスキーマーあるシンプルな再帰的な定義があり期待していますが、あなたは他のアプローチに興味を持っていたと述べたので、take-nthを使用して、以下の機能は、かなりよりコンパクトである。

あなたの例を満たす
(defn chop [coll n] 
    (for [i (range n)] 
    (take-nth n (drop i coll)))) 

:Clojureので

(chop [:a :b :c :d :e :f :g :h :i ] 3) 
;= ((:a :d :g) (:b :e :h) (:c :f :i)) 

(chop [:a :b :c :d :e :f :g :h :i ] 4) 
;= ((:a :e :i) (:b :f) (:c :g) (:d :h)) 

、ライブラリに建てられた、驚くほど遠くにあなたを取得します。それが失敗した場合は、明示的に再帰的な解を使用してください。このバージョンも怠惰です。スタックを吹き飛ばすことなく大きなデータセットを処理するために、任意の "長さ"(明示的に再帰的な)バージョンでlazy-seqまたはloop...recurを使用することをお勧めします。

+0

おかげで、それは素晴らしいです!私はそれが簡単かもしれないとは考えていませんでした。私がこれを理解し始めていると思うようになったとき、私は本当に少ししか示されていません。 –

+0

私は感情を知っている! – JohnJ

+0

@DaveKincaid:再帰を使用するのではなく、既存の高次関数を使用してソリューションをモデル化する必要があります。この種の簡潔なソリューションを考え出すことができます:) – Ankur

0

オリジナルの回答が完全にポイントを逃したため編集されました。

この質問を最初に見たとき、私はclojure.core関数partitionが適用されていると考えました( ClojureDocs pageを参照)。

Daveが指摘したように、パーティションは元の順序で要素に対してのみ機能します。 take-nthソリューションが明らかに優れています。興味のある目的のために、パーティションの種類の作業から得られた複数のシーケンスとのマップの組み合わせ。

(defn ugly-solution [coll n] 
    (apply map list (partition n n (repeat nil) coll))) 

(ugly-solution [:a :b :c :d :e :f :g :h :i] 3) 
;;=> ((:a :d :g) (:b :e :h) (:c :f :i)) 
(ugly-solution [:a :b :c :d :e :f :g :h :i] 4) 
;;=> ((:a :e :i) (:b :f nil) (:c :g nil) (:d :h nil)) 
+0

私はそれを見ましたが、この場合にどのように使用するのか分かりませんでした。常に同じ順序で要素を保持するように見えました。あなたがそれを行う方法を持っているなら、私はそれを見ることにも興味があります。 –

+0

@DaveKincaidはい、あなたは大丈夫です。並べ替えの要件は、パーティションが機能しないことを意味します。私はパーティションを使用する答えを編集しましたが、take-nthは確かに良い方法です。 –

0

私はこのCommon Lispのループを提供する必要があります。

(defun partition-by-nth (list n) 
    (loop :with result := (make-array n :initial-element '()) 
     :for i :upfrom 0 
     :and e :in list 
     :do (push e (aref result (mod i n))) 
     :finally (return (map 'list #'nreverse result)))) 
関連する問題