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

欢迎!请参阅 关于 页面以获取更多关于此如何运作的信息。

+3
Collections

你好!我们最近发现,当将 update-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

选中
 
最佳答案

是的,这是预期行为。在通用映射类型和性能之间存在权衡。考虑了两种选项,但最终决定更关注更常见的类型(散列和数组映射)的性能。

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

刚刚遇到这种无稽之谈。鉴于许多其他功能都这样做了,并且文档字符串中没有提到相反的内容,保持数据类型是合理的预期。函数文档字符串说函数返回“一个新的Map”,但这并不意味着数据类型发生变化。我们处理的是不可变数据结构,当然函数会返回一个新的Map,它几乎不可能做别的。
0

这种设计是有意的,这是为了性能。有序Map主要用於调试。

如果您想使您的排序Map正常工作,请使用此回答底部的代码。

```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')
```

处理排序Map的代码,但速度较慢

```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`不支持排序Map而使用保留排序逻辑,同时保持未排序Map的性能,因此我怀疑这个函数不支持排序Map的背后是否有意图,据我所知,它并未这样记录 :)
我发布的代码已被编辑以在排序Map上工作,(transient (empty m))就是这么做。代码会引发错误。我猜`assoc!`在排序Map上不起作用。

`(assoc! (sorted-map :a :b) :c :d)` 抛出错误
"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
我不明白这个点
> 排序的映射主要用于调试
排序的映射对我来说似乎是一种合适的数据结构。不确定在这里调试的角色在哪里。
...