16

我有以下代码递增向量中每对的第一个元素:

(vec (map (fn [[key value]] [(inc key) value]) [[0 :a] [1 :b]]))

但是我担心这段代码不优雅,因为它首先使用 map 创建一个序列,然后将其转换回向量。

考虑这个类比:

(into [] (map (fn [[key value]] [(inc key) value]) [[0 :a] [1 :b]]))

在#clojure@irc.freenode.net 上,有人告诉我,使用上面的代码很糟糕,因为into展开成(reduce conj [] (map-indexed ...)),在此过程中会产生许多中间对象。然后我被告知实际上into不会扩展到(reduce conj ...)并在可以时使用瞬变。还测量经过的时间表明into实际上比 快vec

所以我的问题是:

  1. map在向量上使用的正确方法是什么?
  2. 当我使用vecinto使用向量时,下面会发生什么?

相关但不重复的问题:

4

1 回答 1

31

实际上,从 Clojure 1.4.0 开始,这样做的首选方法是使用mapv,这就像map除了它的返回值是一个向量。这是迄今为止最有效的方法,根本没有不必要的中间分配。

Clojure 1.5.0 将带来一个新的 reducers 库,它将提供一个通用的方法来创建mapfilter、等takedrop同时创建可用于into []. 您可以在 1.5.0 alpha 版和最近的 ClojureScript 标记版本中使用它。

As for (vec some-seq) and (into [] some-seq), the first ultimately delegates to a Java loop which pours some-seq into an empty transient vector, while the second does the same thing in very efficient Clojure code. In both cases there are some initial checks involved to determine which approach to take when constructing the final return value.

vec and into [] are significantly different for Java arrays of small length (up to 32) -- the first will alias the array (use it as the tail of the newly created vector) and demands that the array not be modified subsequently, lest the contents of the vector change (see the docstring); the latter creates a new vector with a new tail and doesn't care about future changes to the array.

于 2012-08-20T20:17:58.447 回答