Sam*_*tep 4 loops clojure mutable dynamic-arrays data-structures
在Tyler Jennings的演讲"Groupon的引导Clojure"中,从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)]))
Run Code Online (Sandbox Code Playgroud)
(这些都是逐字复制的,包括第二行最后一行的单一缩进.)
他指出,在他使用的基准测试中,上面的第一个函数需要1.1秒,而上面的第二个函数需要0.8秒,因此注意第二个函数优于第一个函数.但是,根据关于瞬态的Clojure文档:
请特别注意,瞬态不是设计为就地瞄准.您必须在下一次调用中捕获并使用返回值.
因此在我看来这个separate-fast-doseq功能是不正确的.但考虑到其他话题的性质,我很难相信这是不正确的.
此separate-fast-doseq功能是否正确使用瞬态?为什么或者为什么不?(如果没有,它的一个例子是什么?)
第二个实现是错误的,因为您怀疑的原因.瞬态集合允许变异以提高效率,但它永远不需要,因此这些conj!调用中的任何一个都可能返回具有不同标识的对象.如果发生这种情况,那么通过丢弃结果conj!,您粘贴的功能将表现不正确.
但是,我无法提供它打破的例子.在Clojure的当前实现中,conj! 确实发生了变化.注意最后的无条件return this.因此,此函数将按预期运行.但是,它依赖于实现细节的正确性,这些细节可能随时发生变化.
有关类似操作的示例,请尝试使用地图而不是向量:
(let [m (transient {})]
(doseq [i (range 20)]
(assoc! m i i))
(count (persistent! m)))
8
Run Code Online (Sandbox Code Playgroud)