Clojure:对嵌套序列进行半展平

时间:2011-03-08 12:19:49

标签: clojure sequence

我有一个包含矢量嵌入列表的列表,如下所示:

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

我该怎么做?我想我需要根据每个列表项的内容创建一个新列表,而不是项目,而这部分我无法从文档中找到如何处理。

5 个答案:

答案 0 :(得分:59)

如果您只想将其展平一级,可以使用concat

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

答案 1 :(得分:27)

要将列表列表转换为包含每个子列表元素的单个列表,您需要{nickis建议的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)

答案 2 :(得分:8)

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

所以我们返回所有不包含序列的序列。

答案 3 :(得分:4)

此外,您可能会发现我在clojuremvc上找到的一般1级展平功能很有用:

(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 :(得分:3)

这是一个功能,无论嵌套不均匀,都会向下平滑到序列级别:

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