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

欢迎!请查看关于 页面以获取更多关于本网站如何运作的信息。

+3
集合

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

被选中
 
最佳答案

是的,这是预期行为。这与映射类型的通用性和性能的权衡有关。两种选择都考虑过,最终决定更注重最常见的类型(哈希和数组映射)的运行效率。

如文档字符串所示,此函数返回“新映射”,并且映射类型不会被保留。

by
刚刚遇到了这个问题。考虑到许多其他函数都保持了类型,保持类型是一个常识性的期望,文档字符串中没有提到相反的内容。函数文档说返回“一个新的映射”并不能表明类型发生了变化。我们处理的是不可变数据结构,当然函数返回一个新的映射,它几乎不可能以其他方式执行。
0
by

这种设计是有意的,是为了性能考虑。有序映射主要用于调试。

如果你的有序映射需要工作,请使用此答案底部的代码。

```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)))
```
by
谢谢,但我看不出`update-vals`为什么不能在有序映射上使用保持排序的逻辑,同时对于无序映射也能保持性能,所以我怀疑这个函数不支持有序映射是否有特定的意图,因为据我所知,它没有做此类记录 :)
by
我发表的代码经过编辑以在有序映射上工作,这就是`(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)
   未为类型{:a :b}定义IEditableCollection.-as-transient协议方法
我不理解这一点。
>排序后的映射主要用于调试
对我来说,排序后的映射似乎是一个合适的数据结构。不确定调试在这里有何作用。
...