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 建议,直接在 APersistentVector$SubVector 中添加了 IKVReduce 接口的 Java 实现。应该审查第二个补丁。两个补丁中包含相同的测试。