web-dev-qa-db-ja.com

Clojure:ネストされたシーケンスのセミフラット化

次のようなベクトルのリストが埋め込まれたリストがあります。

(([1 2]) ([3 4] [5 6]) ([7 8]))

私が知っていることは、一緒に作業するのに理想的ではありません。これを([1 2] [3 4] [5 6] [7 8])にフラット化したいと思います。

flattenは機能しません:それは私に(1 2 3 4 5 6 7 8)を与えます。

どうすればよいですか?アイテムではなく、各リストアイテムのcontentsに基づいて新しいリストを作成する必要があると思います。この部分は、ドキュメントから方法を見つけることができません。

46
ChucK

1レベルだけフラットにしたい場合は、concatを使用できます。

(apply concat '(([1 2]) ([3 4] [5 6]) ([7 8])))
=> ([1 2] [3 4] [5 6] [7 8])
59
nickik

リストのリストをすべてのサブリストの要素を含む単一のリストに変換するには、nickikが示唆するようにapply concatが必要です。

ただし、通常はより良い解決策があります。最初からリストのリストを作成しないでください。たとえば、get-names-forという関数があり、シンボルを受け取り、そのシンボルと呼ぶことができるすべてのクールなもののリストを返すとします。

(get-names-for '+) => (plus add cross junction)

シンボルのリストのすべての名前を取得したい場合は、

(map get-names-for '[+ /]) 
=> ((plus add cross junction) (slash divide stroke))

しかし、これはあなたが抱えていた問題につながります。それらをapply concatで接着することもできますが、最初にmapcatの代わりにmapを使用することをお勧めします。

(mapcat get-names-for '[+ /]) 
=> (plus add cross junction slash divide stroke)
27
amalloy

flattenのコードはかなり短いです。

(defn flatten
  [x]
  (filter (complement sequential?)
    (rest (tree-seq sequential? seq x))))

それは使用しています tree-seqデータ構造をウォークスルーし、原子のシーケンスを返します。すべての最下位シーケンスが必要なので、次のように変更できます。

(defn almost-flatten
  [x]
  (filter #(and (sequential? %) (not-any? sequential? %))
    (rest (tree-seq #(and (sequential? %) (some sequential? %)) seq x))))

したがって、シーケンスを含まないすべてのシーケンスを返します。

8
David Minor

また、私が見つけたこの一般的な1レベルのフラット化関数が便利であることがわかるかもしれません clojuremvc

(defn flatten-1 
  "Flattens only the first level of a given sequence, e.g. [[1 2][3]] becomes
   [1 2 3], but [[1 [2]] [3]] becomes [1 [2] 3]."
  [seq]
  (if (or (not (seqable? seq)) (nil? seq))
    seq ; if seq is nil or not a sequence, don't do anything
    (loop [acc [] [elt & others] seq]
      (if (nil? elt) acc
        (recur
          (if (seqable? elt)
            (apply conj acc elt) ; if elt is a sequence, add each element of elt
            (conj acc elt))      ; if elt is not a sequence, add elt itself 
       others)))))

例:

(flatten-1 (([1 2]) ([3 4] [5 6]) ([7 8])))
=>[[1 2] [3 4] [5 6] [7 8]]

concat例は確かにあなたのために仕事をします、しかしこれはflatten-1コレクション内で非seq要素を許可します:

(flatten-1 '(1 2 ([3 4] [5 6]) ([7 8])))
=>[1 2 [3 4] [5 6] [7 8]]
;whereas 
(apply concat '(1 2 ([3 4] [5 6]) ([7 8])))
=> Java.lang.IllegalArgumentException: 
   Don't know how to create ISeq from: Java.lang.Integer
4
Nevena

ネストが不均一であるかどうかに関係なく、シーケンスレベルまでフラット化する関数は次のとおりです。

(fn flt [s] (mapcat #(if (every? coll? %) (flt %) (list %)) s))

したがって、元のシーケンスが次の場合:

'(([1 2]) (([3 4]) ((([5 6])))) ([7 8]))

それでも同じ結果が得られます。

([1 2] [3 4] [5 6] [7 8])
3
bazeblackwood