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

欢迎!请查看 关于 页面,了解更多关于这个网站的信息。

+2
Collections
找不到之前是否已经有提到,但是看起来 {{empty?}} 判断符对于瞬态集合是损坏的


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?}} 基于 seqability,瞬态不实现。

*建议:* 在 {{empty?}} 中添加一个分支用于 counted? 集合。瞬态实现了 Counted,因此通过这个分支获得支持。其他可计数的集合更快。Seq 分支继续为.SEQ 使用。

性能测试


(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|不再报错|

不确定文档字符串是否应该进行调整以更加通用,特别是 "same as (not (seq coll))",这在现在只适用于 Seqable,不适用于 Counted。我觉得使用 (seq coll) 进行 sequential 检查的建议在这里仍然是好的。

我对其他类似于 Counted 但不是 seqs/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 站点的文档,在列表上支持暂时件没有带来任何好处。

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? 的问题周围使用 cond,通过显式检查是否是 TransientCollection,如果是,则使用 (zero? (count coll)) 作为原始描述中提到的作为 workaround。

目前,暂时件集合没有像持久集合那样实现 Iterable。如果实现了 Iterable,我相信 RT.seqFrom 将会工作,并且通过扩展,empty? 也会工作。

0

评论由:alexmiller 添加

我认为暂时件集合不应该是可迭代的,因为序列隐含着缓存,缓存会影响性能。使用暂时件的原因就是为了批量加载性能。这似乎是适得其反的。迭代器是有状态的,而且我相信仅为了检查空状态而添加它们可能是不好的。

对一个计数器 . colls 空闲性的显式检查将涵盖所有暂时件集合和其他任何计数对象,而不需要产生任何序列。这可能会使所有这些情况更快,并且不需要在实现方面对任何人做出任何新的要求。

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

0
参考:[point to issue](https://clojure.atlassian.net/browse/CLJ-1872)(由 alex+import 报告)
...