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 没有实现方法::kv-reduce 的协议:#'clojure.core.protocols/IKVReduce 错误,类: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 没有实现方法::kv-reduce 的协议:#'clojure.core.protocols/IKVReduce 错误,类: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 实现。应该审查第二个补丁。两个补丁中都包含了相同的测试。