当使用作为“key”参数传递的元素索引在向量上使用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)
用户期望子向量支持所有的正常向量操作,这个异常很令人困惑。问题的原因是因为subvec返回一个clojure.lang.APersistentVector$SubVector,它没有实现clojure.lang.IKVReduce。PersistentVector继承自APersistentVector并实现了IKVReduce,但是SubVector没有获得任何IKVReduce支持。这可能只是一个疏忽。
附上了两个补丁。第一个通过在core.clj中扩展IKVReduce协议来解决问题。这对Clojure用户来说很方便,可以作为替代方案直接将其添加到他们的代码中。第二个补丁,如Alex Miller建议的,将IKVReduce接口的Java实现直接添加到APersistentVector$SubVector中。应该审查的是第二个补丁。两个补丁中都包含相同的测试。