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?}} 中为计数的coll添加一个分支。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|error|0.19 ms|不再出错|
|t1|error|0.20 ms|不再出错|

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

我对其他计数但不是seq/Seqable的类型进行了快速浏览,没有发现太多,除了像ChunkBuffer之类的内部事物。许多类型既是又是,因此会使用计数的路径(例如所有的持久集合和任何类型的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 页面上的文档,在列表上支持transients没有任何好处。

当前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 中,当前补丁的方法是在 empty? 中通过显式检查它是否是 TransientCollection 来解决该问题,并像原始描述中提到的作为解决方案使用 (zero? (count coll))

当前,transient 集合没有实现 Iterable,而持久的集合实现了。如果实现 Iterable,我相信 RT.seqFrom 将会工作,并且通过扩展,empty?

0

评论由:alexmiller发表

我认为,transient 集合不应实现 Sequable 有很好的理由——sequence 意味着缓存,缓存会影响性能,而使用 transient 的全部目的就是为了批量加载性能。这似乎是适得其反的。迭代器是有状态的,而且我认为仅仅为了检查空状态而添加它可能是不好的。

对 counted? colls 的显式空值检查将覆盖所有 transient 集合以及其他任何无 sequence 的计数值。这可能对所有这些情况都更快,而且不需要在实现上对任何人做新要求。

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

0
参考: https://clojure.atlassian.com/browse/CLJ-1872 (由 alex+import 提出)
...