2024 年 Clojure 状态调查! 中分享您的想法。

欢迎!请参阅 关于 页面以获取更多关于如何使用本页面的信息。

+11 投票
Collections
已关闭

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

附注:已在 1.11.0-alpha3 中修复

10 答案

0 投票

[email protected] 发布的评论:

以下是我的当前解决方案

`
(扩展类型 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)))

`

在我的测试中,通常是将子向量复制到常规向量中更快,但我更喜欢transduce修复的样式。可能最好在APersistentVector.java中添加一个本地Java实现。如果Clojure/core团队想要补丁,我愿意去完成这项工作。

0 投票
by
_评论人:[email protected]_

使用更多互操作进行修正,以获得更好的性能。与常规向量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 投票
by

[email protected] 发布的评论:

支持对SubVector的IKVReduce

0 投票
by

[email protected] 发布的评论:

补丁还增加了一个测试,用于在常规向量和子向量上执行reduce-kv。

0 投票
by

评论者:alexmiller

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

0 投票
by

[email protected] 发布的评论:

在SubVector上实现IKVReduce的Java版本

0 投票
by

[email protected] 发布的评论:

是的,这很有意义。我犹豫了一下,因为我不想重新构建层次以使 SubVector 成为 PersistentVector 的子类,但这并不是修复该错误所必需的。CLJ-2065-Support-IKVReduce-on-SubVector.patch 仅是 Java 方面,加上相同的测试。

0 投票
by

[email protected] 发布的评论:

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

0 投票
by
0 投票
by

JIRA 工单略微过时。它只说明错误影响 1.9,但我可以确认它仍然存在于 1.10.3。

by
是的,通常我们假设这只是“在哪里注意到”的,除非因为有回归或其他原因的进一步说明,所以没什么大不了的。
...