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 No implementation of method: :kv-reduce of protocol: #'clojure.core.protocols/IKVReduce found for class: clojure.lang.APersistentVector$SubVector 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将返回一个subvec而不是复制。
(reduce-kv + 0 (vec (subvec (link: 1 2 3) 1)))
IllegalArgumentException No implementation of method: :kv-reduce of protocol: #'clojure.core.protocols/IKVReduce found for class: clojure.lang.APersistentVector$SubVector clojure.core/-cache-protocol-fn (core_deftype.clj:583)
Clojure用户期望子向量支持所有正常的向量操作,而这个异常很令人困惑。问题的原因是子向量返回了一个 clojure.lang.APersistentVector$SubVector,它没有实现 clojure.lang.IKVReduce。PersistentVector继承自 APersistentVector 并实现了 IKVReduce,但 SubVector 没有得到任何 IKVReduce 支持。这很可能是疏忽。
附上了两个补丁。第一个补丁通过在 core.clj 中扩展 IKVReduce 协议来修复了这个问题,这对于Clojure用户来说作为解决方案很方便。第二个补丁,由Alex Miller建议,直接在 APersistentVector$SubVector 中添加了 IKVReduce 接口的 Java 实现。应该审查的是第二个补丁。两个补丁中包含相同的测试。