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

欢迎!有关如何使用本站的更多信息,请参阅 关于 页面。

+3 投票
Collections

大家好!我们最近发现,当将 `update-vals` 应用于排序后的映射时,由于集合不可转为瞬时集,它会被转换回未排序的数组/散列表映射。以下是一个示例。值得注意的是,这与 `medley/map-vals` 不同,`medley/map-vals` 保留排序,这是我们之前在更新到 1.11 之前使用该功能时所使用的。

这是预期的行为吗?如果是的话,我很好奇背后的意图 :) 感谢!

user> (def m (sorted-map :a 0 :b 0))
;; => #'user/m
user> (type m)
;; => clojure.lang.PersistentTreeMap
user> (type (update-vals m inc))
;; => clojure.lang.PersistentArrayMap
user> (type (medley.core/map-vals inc m))
;; => clojure.lang.PersistentTreeMap

2 个答案

+3 投票

被选中
 
最佳答案

是的,这是预期的行为。考虑到了映射类型的一般性和性能的权衡。已经考虑了两种选项,最终的决定是更关注更常见的类型(哈希和数组映射)的性能。

如文档字符串所述,此函数返回“一个新映射”,而映射类型并不保留。

刚刚被这个无聊的问题困扰。考虑到许多其他函数都这样做,保留类型是逻辑上的期待,文档字符串中也没有任何相反的说明。文档字符串称该函数返回“一个新的映射”,但这并不能表明类型发生了变化。我们处理的是不可变的数据结构,当然函数返回一个新的映射,它无法做到其他。
0 投票

这种设计是有意为之,它是为了性能。排序映射主要用于调试。

如果您想让排序映射工作,请使用本回答底部的代码。

```clojure
(defn update-vals
  "m f => {k (f v) ...}

  Given a map m and a function f of 1-argument, returns a new map where the keys of m
  are mapped to result of applying f to the corresponding values of m."
  {:added "1.11"}
  [m f]
  (with-meta
    (persistent!
      (reduce-kv (fn [acc k v] (assoc! acc k (f v)))
        (if (instance? clojure.lang.IEditableCollection m)
          (transient m)
          (transient (empty m)))
        m))
    (meta m)))

(update-vals
  (sorted-map
    :d :e
    :b :c
    :h :a
    :a :g
    )
  str)
```

错误

```clojure
1. Unhandled java.lang.ClassCastException
   class clojure.lang.PersistentTreeMap cannot be cast to class clojure.lang.IEditableCollection
   (clojure.lang.PersistentTreeMap and clojure.lang.IEditableCollection are in unnamed module of loader 'app')
```

用于排序映射的代码,但速度较慢

```clojure
(defn update-vals
  "m f => {k (f v) ...}

  Given a map m and a function f of 1-argument, returns a new map where the keys of m
  are mapped to result of applying f to the corresponding values of m."
  {:added "1.11"}
  [m f]
  (with-meta
    (reduce-kv (fn [acc k v] (assoc acc k (f v)))
      (empty m)
      m)
    (meta m)))
```
感谢,但我看不出update-vals为何不能在排序映射中使用排序保持逻辑,同时还能保持非排序映射的性能,所以我好奇这个函数为何不支持排序映射,因为没有这样的文档记载,据我所知:)
我发布的代码经过编辑,以便在排序映射上工作,这就是`(transient (empty m))`的作用。该代码会发生错误。assoc!似乎不能在排序映射上工作。

`(assoc! (sorted-map :a :b) :c :d)`会发生错误
头像
"assoc!" 是用于短暂数据的。请参阅API文档:https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/assoc!
头像
我在第一个例子中使用了短暂数据。虽然在第二个例子中我出现了错误。
头像
=>(assoc! (transient (sorted-map :a :b)) :c :d)
   对于类型 object: {:a :b},未定义 IEditableCollection.-as-transient 协议方法
头像
我不理解这个观点
> 排序映射主要用于调试
对我来说,排序映射似乎是一个合适的数据结构。不确定调试在这里有什么作用。
...