Clojure中的一级序列压平函数是什么?

42

在Clojure中,一级序列展平函数是什么? 我目前正在使用apply concat,但我想知道是否有内置函数可用,无论是在标准库还是clojure-contrib中。


也许这是我在答案中提供的链接的重复?如果没有更好的答案出现,我会投票关闭。 - andrew cooke
可能是 https://dev59.com/zG435IYBdhLWcg3wvy6_ 的重复问题。 - Brad Koch
3个回答

26

我的通常首选是apply concat。此外,不要忽视(for [subcoll coll, item subcoll] item)——根据上下文,这可能会导致更清晰的代码。


1
好的,我认为 mapcat identity 就是我需要的。感谢您的回答! - missingfaktor
3
mapcat被实现为apply concat https://github.com/clojure/clojure/blob/clojure-1.7.0/src/clj/clojure/core.clj#L2652,通过将参数处理为序列来实现惰性求值的魔法。 - Timothy Pratley

22

没有标准函数。apply concat在许多情况下都是一个好的解决方案。或者你可以等效地使用mapcat seq

apply concat的问题是,当除了集合/序列之外的任何其他内容位于第一层时,它会失败:

(apply concat [1 [2 3] [4 [5]]])
=> IllegalArgumentException Don't know how to create ISeq from: java.lang.Long...

因此,您可能想要执行以下操作:
(defn flatten-one-level [coll]  
  (mapcat  #(if (sequential? %) % [%]) coll))

(flatten-one-level [1 [2 3] [4 [5]]])
=> (1 2 3 4 [5])

作为一个更一般的观点,缺乏内置函数通常不应该阻止你定义自己的函数 :-)

1
谢谢您的回答。我想要apply concat所提供的行为。 - missingfaktor

10
我也使用apply concat——我认为核心中没有其他东西了。 flatten是多级的(并且是通过树遍历定义的,而不是通过重复单层展开定义的)。
另请参见Clojure: Semi-Flattening a nested Sequence ,其中包含来自clojure mvcflatten-1(比我预期的要复杂得多)。 更新以澄清惰性:
user=> (take 3 (apply concat (for [i (range 1e6)] (do (print i) [i]))))
012345678910111213141516171819202122232425262728293031(0 1 2)

你可以看到它会对参数进行32次评估——这是为了提高效率而进行的分块处理,并且在其他情况下是惰性的(不会评估整个列表)。有关分块的讨论,请参阅http://isti.bitbucket.org/2012/04/01/pipes-clojure-choco-1.html末尾的注释。


我认为应该很少使用 apply,这种用法是否合理?它不比必要的方法更慢吗? - missingfaktor
我能想到的唯一理由是,如果存在单一替代方案,则应使用该替代方案,因此才说它应该很少使用。至于速度,我不清楚,但不要期望它特别慢。 - andrew cooke
1
绝对不是应该很少使用apply的情况。想用就用吧;在你的意大利面上随便撒点。序列是惰性求值的,包括函数参数序列,所以(apply (fn [& args] (first args)) (range 1e5))仍然非常快,只需将函数指针传递给列表的起始位置。concat具有类似的“形状”,因此同样便宜。 - amalloy
值得知道的是,即使没有进行分块,apply函数至少会实现4个元素。https://dev59.com/tq3la4cB1Zd3GeqPS9It - Didier A.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接