reduce-kv 需要将元素索引传递给"key"参数时,在向量上按预期工作。然而,它在 subvec 上失败,因为 clojure.lang.APersistentVector$SubVector 没有实现 IKVReduce。
(reduce-kv + 0 (link: 1 2 3))
9
(reduce-kv + 0 (subvec (link: 1 2 3) 1))
IllegalArgumentException 没有为类: clojure.lang.APersistentVector$SubVector 的协议: #'clojure.core.protocols/IKVReduce 找到方法: :kv-reduce clojure.core/-cache-protocol-fn (core_deftype.clj:583)
一个解决办法是将 subvec 复制到一个向量中
(reduce-kv + 0 (into (link: ) (subvec (link: 1 2 3) 1)))
6
但请注意,vec 不会在这里工作。从 Clojure 1.7 开始,vec 将返回一个 subvec 而不是复制。
(reduce-kv + 0 (vec (subvec (link: 1 2 3) 1)))
IllegalArgumentException 没有为类: clojure.lang.APersistentVector$SubVector 的协议: #'clojure.core.protocols/IKVReduce 找到方法: :kv-reduce clojure.core/-cache-protocol-fn (core_deftype.clj:583)
Clojure 用户期望子向量支持所有正常的向量操作,而这个异常让人困惑。问题的原因是 subvec 返回一个不实现 clojure.lang.IKVReduce 的 clojure.lang.APersistentVector$SubVector。PersistentVector 继承自 APersistentVector 并实现了 IKVReduce,但 SubVector 没有获得任何 IKVReduce 支持这似乎只是一个疏忽。
附上两个补丁。第一个补丁通过在 core.clj 中扩展 IKVReduce 协议来解决问题,这对于 Clojure 用户来说是一个方便的解决方案。第二个补丁(由 Alex Miller 建议),直接将 IKVReduce 接口的 Java 实现添加到 APersistentVector$SubVector。应该审查第二个补丁。同样的测试包含在两个补丁中。