Clojure:半展平嵌套序列

Clojure:半展平嵌套序列,clojure,sequence,Clojure,Sequence,我有一个包含嵌入向量列表的列表,看起来像: ([12])([34][56])([78])) 我知道这不是理想的合作方式。我想把它展平到([12][34][56][78]) 展平不起作用:它给了我(12345678) 我该怎么做?我想我需要根据每个列表项的内容创建一个新列表,而不是项目,而这部分我无法从文档中找到如何操作。如果您只想将其展平一级,可以使用concat (apply concat '(([1 2]) ([3 4] [5 6]) ([7 8]))) => ([1 2] [3 4]

我有一个包含嵌入向量列表的列表,看起来像:

([12])([34][56])([78]))

我知道这不是理想的合作方式。我想把它展平到
([12][34][56][78])

展平不起作用:它给了我
(12345678)


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

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

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

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

flatte
的代码相当短:

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

因此,我们将返回所有不包含序列的序列。

此外,您可能会发现我在上找到的这个通用1级展平函数很有用:

例如:

(flatten-1 (([1 2]) ([3 4] [5 6]) ([7 8])))
=>[[1 2] [3 4] [5 6] [7 8]]
concat
exampe当然可以为您完成这项工作,但此
flatte-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

无论嵌套是否不均匀,下面的函数都将平滑到序列级别:

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

对mapcat来说是+1——这导致了一个比其他方法更优雅的解决方案。很高兴知道。谢谢我使用的是
映射索引
,但是没有
mapcat索引
@PostSelf
映射索引
只是向
映射
添加参数的简写。不要使用
(应用concat(映射索引的f xs))
,而是尝试
(映射cat f(范围)xs)
@amalloy太棒了!我意识到我根据每对中的第二个元素做了一个
过滤器
,所以除非有方法合并它,否则我必须将其保留为is@PostSelf当然有
mapcat
是一种“基本”列表操作,许多其他操作都可以在其上构建。您可以编写
(mapcat(fn[ix](let[x')(when(pred x')[ix'])(range)xs)
(def balls'(:a1)(:b1)(:c1)))
不完全是:(apply concat balls)=>(:a1(:b1)(:c1))完美:(几乎平坦的球)=>(:a1)(:b1(:c 1))
([1 2] [3 4] [5 6] [7 8])