这是瞬变的正确用法吗?
在谈话"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