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

已被选中
 
最佳答案

是的,这是预期行为。在映射类型通用性和性能方面存在权衡。两种选择都已被考虑,最终决定以更常见的类型(哈希和数组映射)的性能为更高关注点。

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

刚刚遇到了这个无聊的问题。考虑到许多其他函数都保持了类型,保持类型是一个逻辑预期,文档字符串中也无一语反对。docstring中提到函数返回“新映射”,但这并不表明类型会改变。我们当然在处理不可变数据结构,因此函数返回新的映射,几乎不可能做其他事情。
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)
   对于类型 {:a :b} 未定义协议方法 IEditableCollection.-as-transient
我不理解这个点
> 按序存储主要适用于调试
我认为按序存储是一个合适的数据结构。不确定在这里调试应该在哪里进行。
...