请分享您的观点,参加2024 Clojure现状调查!

欢迎!请参阅关于页面,了解更多此网站的工作方式。

+2
集合
找不到之前是否提出过此问题,但似乎{{empty?}}谓词对transient集合不适用


user=> (empty? (transient []))
IllegalArgumentException 无法从clojure.lang.PersistentVector$TransientVector创建ISeq: clojure.lang.RT.seqFrom (RT.java:528)

user=> (empty? (transient {}))
IllegalArgumentException 无法从clojure.lang.PersistentArrayMap$TransientArrayMap创建ISeq: clojure.lang.RT.seqFrom (RT.java:528)

user=> (empty? (transient #{}))
IllegalArgumentException 无法从clojure.lang.PersistentHashSet$TransientHashSet创建ISeq: clojure.lang.RT.seqFrom (RT.java:528)


解决方案是使用{{(zero? (count (transient ...)))}}检查代替。

*原因: * {{empty?}}基于可序列性,而transients不实现。

*建议: * 在{{empty?}}中添加一个分支,用于计数的colls。Transients实现Counted,因此可以通过此分支获得支持。其他计数的coll更快。序列分支继续为序列执行。

性能测试


(def p [])
(def p1 [1])
(def t (transient []))
(def t1 (transient [1]))

;; 记录所有这些操作的时间
(dotimes [i 20] (time (dotimes [_ 10000] (empty? p))))
(dotimes [i 20] (time (dotimes [_ 10000] (empty? p1))))
(dotimes [i 20] (time (dotimes [_ 10000] (empty? t))))
(dotimes [i 20] (time (dotimes [_ 10000] (empty? t1))))


结果

||coll||before||after||result||
|p|0.72 ms|0.08 ms|为空时速度快得多|
|p1|0.15 ms|0.13 ms|非空时略有提升|
|t|错误|0.19 ms|不再报错|
|t1|错误|0.20 ms|不再报错|

不确定是否应该调整文档字符串使其更通用,特别是“同一于(not (seq coll))”,现在只对Seqable有效,但对Counted无效。我认为仍然建议使用(seq coll)进行序列检查。

我浏览了其他一些是Counted但不是序列/Seqable的类型,但没有找到多少东西,比如内部事物比如ChunkBuffer。很多既可以是又可以不是,因此会使用计数的路径(例如所有持久化的colls和任何类型的IndexedSeq)。

我想另一种选择是将empty?完全转换为关于(zero? (bounded-count 1 coll))的函数,并完全依赖count的多态性。

*补丁: * clj-1872.patch

5 答案

0

评论由: alexmiller 所发表

可能类似于 CLJ-700。

0

评论由: devn 所发表

正如 CLJ-700 中所述,这是一个不同的问题。

0

评论由: devn 所发表

首先,原始描述带来了 (empty? (transient ()))。根据 https://clojure.org/reference/transients 中的文档,对于列表,支持transient没有好处。

当前对于Java集合的行为

`
(empty? (java.util.HashMap. {}))
=> true

(empty? (java.util.HashMap. {1 2}))
=> false

(seq (java.util.HashMap. {1 2}))
=> (#object[java.util.HashMap$Node 0x4335c9c3 "1=2"])

(seq (java.util.HashMap. {}))
=> nil
`

Java数组也有相同的行为。

在 CLJS-2802 中,当前补丁的方法是通过显式检查是否是 TransientCollection,并在 empty? 中使用该方法来解决问题。

目前,transient集合不实现 Iterable,像持久集合那样做的。如果实现了 Iterable,我相信 RT.seqFrom 将会工作,从而也使得 empty? 能够工作。

0

评论由: alexmiller 所发表

我认为transient集合不应该实现Seqable有很好的理由——seqs暗示了缓存,缓存损害性能,而使用transient的整体原因是批量加载性能。这似乎是得不偿失的。迭代器是有状态的,我又怀疑为了检查empty?而添加它可能是件坏事。

对计数的colls显式检查空状态将涵盖所有transient colls和任何其他计数的,而不需要创建一个seq。这可能在这些情况下更快,而且不需要在实现中进行任何新操作。

另一个选择是提供一个 IEmptyable 接口和/或协议来指明显式空检查的支持。可能有些过度。

0
by
参考: https://clojure.atlassian.net/browse/CLJ-1872(由 alex+import 报告)
...