请在 2024 Clojure调查问卷 中分享您的想法!

欢迎!请查看 关于 页面,了解更多关于此工作方式的信息。

+2
序列
编辑

在使用reducers时,我偶尔会使用 foldcat 通过reducer链“实现”转换后的集合。对于后续的代码,最好使用一个完全实现的标准的 clojure 集合,如 vec。当为了性能原因将核心集合代码更新为使用reducers时,这通常是这样的。

以下代码可以正常工作:

(->> (repeat 2 3) (vec) (r/map inc) (r/foldcat) (vec))
=> [4 4]

然而,当输入足够大时,这会触发并行行为,将返回类型从 Array 更改为 Cat,导致最后一个 vec 扛不住。

(->> (repeat 513 3) (vec) (r/map inc) (r/foldcat) (vec))
Execution error at user/eval19976 (form-init6687476487684153971.clj:1).
Unable to convert: class clojure.core.reducers.Cat to Object[]

我认为“正确”的方式是使用 (into []) 而不是在最后一个位置使用 vec,这样效果很好。

话虽如此,这种行为相当令人惊讶,因为 seqintoArrayCat 上都能很好地工作。这也非常危险,因为这样的代码可以在生产环境中安静地运行,直到输入数据超过某个阈值。为什么 seq 能在 Cat 上工作,而 vec 不能,有现实的原因吗?

1 答案

+2

看起来这里遗漏了一个未被覆盖的用例 - vec 最初委托给 LazilyPersistentVector.create(),它没有捕获任何覆盖的用例,最终跌入尝试数组的接收。

可用的创建 PV 的用例(自 1.7 起有)包括:

  • IReduceInit - 自身减少
  • ISeq - 通过 seq 进行遍历
  • Iterable - 通过迭代进行遍历
  • 对象数组接收

Cat 对象是可减少的(通过 CollReduce 协议,该协议在 LPV 中不可用,因为它是用 Java 编写的),是可遍历的(但不是可遍历的对象),也是可折叠的。Cat 优于 IReduceInit 和 vec(CLJ-1546)的出现,两者都在 1.7.0 中,但是尝试使用较旧的版本,似乎这一直都是损坏的。

看起来 Cat 或 LPV 都应该做更多的事情,以便 Cat 能够被 vec 它。需要评估确定哪个路径更好。我的第一印象是 Cat 做了它所宣传的事情,而 LPV 是缺失一个用例。

记录在 https://clojure.atlassian.net/browse/CLJ-2785

感谢你详细的解释 @alexmiller!我很想了解你们采取的哪种方法。
...