reduce-kv在将元素索引作为“键”参数传递的向量上按预期工作。然而,当使用subvec时它会失败,因为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)
一种解决方法是复制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 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用户期望子向量支持所有正常的向量操作,而这个异常让人困惑。问题产生的原因是subvec返回的clojure.lang.APersistentVector$SubVector没有实现clojure.lang.IKVReduce。PersistentVector从APersistentVector继承并实现了IKVReduce,但SubVector没有得到IKVReduce支持。这很可能是疏忽造成的。
附上了两个补丁。第一个补丁通过在core.clj中扩展IKVReduce协议来解决这个问题,这对于Clojure用户来说非常方便,可以直接将其添加到代码中作为解决方案。第二个补丁,由Alex Miller建议,直接将IKVReduce接口的Java实现添加到APersistentVector$SubVector中。第二个补丁是应该被审查的。两个补丁中包含相同的测试。