请在 2024 年 Clojure 状态调查! 中分享您的看法。

欢迎!请参阅 关于 页面以了解更多此平台的工作方式。

+11
集合
已关闭

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 实现。应该审查第二个补丁。两个补丁中包含相同的测试。

已关闭,备注: 在 1.11.0-alpha3 中修复

10 个答案

0

评论来自:[email protected]

以下是我的当前解决方案

`
(extend-type clojure.lang.APersistentVector$SubVector
clojure.core.protocols/IKVReduce
(kv-reduce [subv f init]

(transduce (map-indexed vector)
           (fn ([ret] ret) ([ret [k v]] (f ret k v)))
           init
           subv)))

`

在我的测试中,将 subvec 复制到常规向量通常更快,但我喜欢 transduce 修复的外观。如果在 APersistentVector.java 中添加原生的 Java 实现,可能会更快。如果 Clojure/core 团队需要补丁,我愿意完成这项工作。

0
_评论来自:[email protected]_

使用更多 interop 提高性能的修订版解决方案。  速度可与常规向量的 reduce-kv 相比拟。


(when-not (satisfies?   clojure.core.protocols/IKVReduce (subvec [1] 0))
  (extend-type clojure.lang.APersistentVector$SubVector
    clojure.core.protocols/IKVReduce
    (kv-reduce
      [subv f init]
      (let [cnt (.count subv)]
        (loop [k 0 ret init]
          (if (< k cnt)
            (let [val (.nth subv k)
                ret (f ret k val)]
              (if (reduced? ret)
                @ret
                (recur (inc k) ret)))
            ret))))))
0

评论来自:[email protected]

支持 SubVector 的 IKVReduce

0

评论来自:[email protected]

补丁还添加了在常规向量和子向量上执行 reduce-kv 的测试。

0

评论来自:alexmiller

为什么不直接在 APersistentVector$SubVector 中实现 IKVReduce?

0

评论来自:[email protected]

Java 实现对 SubVector 的 IKVReduce

0

评论来自:[email protected]

是的,这很有意义。我犹豫了一下,因为我不想重写层次结构使 SubVector 成为 PersistentVector 的子类,但这并不是修复错误的必要条件。CLJ-2065-Support-IKVReduce-on-SubVector.patch 只是在 Java 方面,加上相同的测试。

0

评论来自:[email protected]

更新补丁,将 nth() 调用内联,避免了不必要的边界检查。

0
0

JIRA 任务 有些过时。它只说这个错误影响 1.9,但我可以确认它仍在 1.10.3 中存在。

是的,我们通常假定这只是一个“观察到的地方”,除非由于回归或其他原因有进一步的阐述,所以没有什么大不了的。
...