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

欢迎!请参阅关于页面了解此站点的更多信息。

+3
集合

你好!我们最近发现,当将update-vals应用于有序映射时,由于集合不可转为transient,它会转换为无序的数组/哈希映射。下面给出一个例子。顺便说一下,这与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)`发生错误。
by
"assoc!" 用于临时代码。请参阅 API 文档:https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/assoc!
by
我在第一个示例中使用了临时代码。虽然第二个例子中我搞错了。
by
=>(assoc! (transient (sorted-map :a :b)) :c :d)
   对于类型 {:a :b} 未定义 IEditableCollection.-as-transient 协议方法
by
我不懂这个观点。
sorted maps 主要用于调试
在我看来,sorted maps 似乎是一种合理的数据结构。不确定调试在这里如何定位。
...