这是瞬变的正确用法吗?

在谈话"Bootstrapping Clojure at Groupon" by Tyler Jennings,从25:14到28:24,他讨论了separate功能的两种实现方式,都采用瞬变:这是瞬变的正确用法吗?

(defn separate-fast-recur [pred coll] 

(loop [true-elements (transient [])

false-elements (transient [])

my-coll coll]

(if (not (empty? my-coll))

(let [curr (first my-coll)

tail (rest my-coll)]

(if (pred curr)

(recur (conj! true-elements curr) false-elements tail)

(recur true-elements (conj! false-elements curr) tail)))

[(persistent! true-elements) (persistent! false-elements)])))

(defn separate-fast-doseq [pred coll]

(let [true-elements (transient [])

false-elements (transient [])]

(doseq [curr coll]

(if (pred curr)

(conj! true-elements curr)

(conj! false-elements curr)))

[(persistent! true-elements) (persistent! false-elements)]))

(这些都是逐字复制,包括在最后unidiomatic缩进)

他指出,在他使用的基准测试中,上面的第一个函数花了1.1秒,而上面的第二个函数花了0.8秒,注意到第二个函数因此优于第一个函数。然而,根据Clojure documentation on transients:

特别要注意,暂态不是设计在现场。您必须在下次调用中捕获并使用返回值。

因此,在我看来,这个separate-fast-doseq函数是不正确的。但考虑到谈话其余部分的性质,我很难相信这是不正确的。

这是separate-fast-doseq功能瞬变的正确使用?为什么或者为什么不?(如果不是,那是什么突破的例子吗?)

回答:

第二种方案是不正确的正是你所怀疑的原因。瞬态收集允许变异本身的效率,但它永远不会需要来,所以这些conj!电话中的任何一个可能会返回一个对象,具有不同的身份。如果出现这种情况,则通过丢弃的conj!的结果,您粘贴功能会出现错误的行为。

但是,我不能提供它打破的例子。在current implementation of Clojure,因为它发生,conj!始终变异本身就位。注意最后无条件return this。因此,此功能将按预期行事。然而,它依赖于实现细节的正确性,这些细节可能随时改变。

对于类似的排序,其不休息操作的一个例子,尝试使用而不是矢量地图:

(let [m (transient {})] 

(doseq [i (range 20)]

(assoc! m i i))

(count (persistent! m)))

8

以上是 这是瞬变的正确用法吗? 的全部内容, 来源链接: utcz.com/qa/258827.html

回到顶部