我有一个嵌套向量列表的列表,看起来像这样:
(([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)
。
我该怎么办?我想我需要根据每个列表项的内容而不是项创建一个新的列表,并且从文档中找不到如何执行这一步骤。
如果你只想把它展开一层,你可以使用concat
(apply concat '(([1 2]) ([3 4] [5 6]) ([7 8])))
=> ([1 2] [3 4] [5 6] [7 8])
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)
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))))
因此,我们返回不包含序列的所有序列。
你可能会发现这个通用的一级展平函数很有用,我在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
也允许集合中包含非序列元素:
(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
(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])
map-indexed
,但是没有mapcat-indexed
。 - Post Selfmap-indexed
只是为map
添加一个参数的简写。不要使用(apply concat (map-indexed f xs))
,而是尝试使用(mapcat f (range) xs)
。 - amalloyfilter
,所以除非有一种方法可以将其合并,否则我必须保持原样。 - Post Selfmapcat
是一种“基本”的列表操作,许多其他操作都是基于它构建的。你可以使用(mapcat (fn [i x] (let [x' (f x)] (when (pred x') [i x']))) (range) xs)
代替(apply concat (filter (comp pred second) (map-indexed f xs)))
。 - amalloymapcat
是最有价值的球员。 - Alper