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