reduce-kv在传递元素索引作为"key"参数的向量化工作正常。然而,在子向量化时它会失败,因为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将返回一个子向量而不是复制。
(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建议,直接将IKVReduce接口的Java实现添加到APPersistentVector$SubVector中。应该审阅第二个补丁。这两个补丁都包含了相同的测试。