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