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 用户期望子向量支持所有的正常向量操作,这个异常很令人费解。问题的原因是 subvec 返回的 clojure.lang.APersistentVector$SubVector 没有实现 clojure.lang.IKVReduce。PersistentVector 从 APersistentVector 继承并实现了 IKVReduce,但 SubVector 没有得到任何 IKVReduce 支持。这可能只是一个疏忽。
附上了两个补丁。第一个补丁通过扩展 core.clj 中的 IKVReduce 协议来修复了问题。这对于 Clojure 用户来说作为一个解决方案非常方便。第二个补丁,由 Alex Miller 建议,将 Java 实现 IKVReduce 接口直接添加到 APersistentVector$SubVector。应该审查第二个补丁。两次补丁中都包含相同的测试。