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

欢迎!请查看 关于 页面以获取更多信息。

+6
Collections
retagged

updateswap! 等中的 & args 非常人性化,并且具有良好的组合能力,所以我很惊讶地发现 update-keysupdate-vals 没有,它们只是 [m f]

这是为什么?

我认为将 & args 添加到这些函数会使它们与现有的核心函数更一致,并有助于避免不必要的匿名函数。

1 答案

+1
by

现有的实用库中有许多类似于 update-keysupdate-vals 的函数(通常称为 map-keys / map-vals)。据我所知,那些函数还没有 update 的额外参数语义,而且这不是人们提出的要求。

一个区别是 update 等调用 f 对单个值,而 update-keysupdate-vals 调用 f 对多个值。我不确定你经常希望为每个键或值传递相同的静态尾随参数到要应用的功能。我没有那个例子(但也许这只是因为现有的函数还没有这样做)。

当然,一个解决方案是使用 partial 或匿名函数将这些参数推入 f 中,或许这可以作为一种搜索方法。

你是否有具体的使用场景需要用到这个功能?

你可以找到一些示例:

* https://grep.app/search?q=map_keys%20%23%28&filter[lang][0]=Clojure
* https://grep.app/search?q=map_vals%20%23%28&filter[lang][0]=Clojure

虽然这些示例中的并不完全符合需要的模式,但确实有一些是满足要求的。

修改
由于 util 库通常将这些操作视为序列函数,所以当添加 `& args` 参数时属性运算符无法正常工作,我想这可能是一个原因。

我的特定用例是在处理一个 atom 中的嵌套结构时,当完成记录的处理后,我希望保留其余数据。例如:

```
(def *state (atom {"table" {:schema (,,,) :records [,,,]}}))

;; 我没有预料到会发生这种错误。
(swap! *state update-vals assoc :records [])

;; 这可行,但不太优雅。
(swap! *state update-vals #(assoc % :records []))
```

我在野外的这些示例中找到了这种用法可以很有用:

* https://github.com/penpot/penpot/blob/develop/frontend/src/app/worker/import.cljs#L351
* https://github.com/penpot/penpot/blob/develop/backend/src/app/rpc/queries/files.clj#L273
* https://github.com/clojure/tools.analyzer/blob/master/src/main/clojure/clojure/tools/analyzer/passes/uniquify.clj#L31
* https://github.com/exoscale/deps-modules/blob/master/src/exoscale/deps_modules.clj#L154
* https://github.com/exoscale/deps-modules/blob/master/src/exoscale/deps_modules.clj#L156

在实际应用中,相对于 `update-keys`,`update-vals` 可能更有用,但在构造示例中,你可能想要将某个后缀应用到 map 的所有键上。

```
(update-keys {"table" {}} str "__v1") => {"table_v1" {}}
````
by
编辑 by
[编辑:如何在网站上正确格式化代码?我尝试了,但无法解决这个问题,因此我只是把它当作markdown格式。]

您还考虑添加 `update-keys` 和 `update-vals` 的可变参数情况吗?我在发现我的用例不支持后感到惊讶,并且我认为我有一个相当有说服力的例子表明这是必要的。

我有一个类似这样的数据结构(以及稍后要使用的谓词)
```
(def data {:foo {0 {:bar [10 42 11 12]}
                 1 {:bar [20 21 42 22]}
                 ,,, }})

(def my-special-pred (complement #{42}))
```

(注意 `(data :foo)` 中可以有不定数量的项目;三个逗号应类似省略号。)

假设我想更新 `:foo 0 :bar` 中的向量。这可以通过以下方式完成
```
(update data :foo update 0 update :bar (partial filter my-special-pred))
```

在这种情况下,您也可以只使用 `update-in`(您可能应该这样做)。但是,如果您想对 `:foo` 映射中的所有值都这样做,而不仅仅是0的情况怎么办?您应该能够只使用
```
(update data :foo update-vals update :bar (partial filter my-special-pred))
```

但不行,因为 `update-vals` 只接受两个参数。相反,您需要执行类似以下操作
```
(update data :foo update-vals #(update % :bar (partial filter my-special-pred)))
```

这比最初看起来更不方便;由于匿名函数字面量不允许嵌套,您不能使用另一个一个函数(例如)作为谓词。但是,如果您有一个可变参数的 `update-vals`,您就可以这样做
```
(update data :foo update-vals update :bar (partial filter #(not= 0 (mod % 42))))
```

出于同样的原因,您不能轻易地使用多个“`update-vals`”层级,如下所示
```
(def data2 {0 {:top 200
               :bottom 201
               ,,, }
            1 {:left 300
               :right 301
               ,,, }})
(update-vals data2 update-vals inc)
```

有各种解决方法,但最干净、最佳的方法似乎就是自己编写一个包装现有函数的 `update-vals` 函数
```
(defn update-vals [m f & args]
  (clojure.core/update-vals m #(apply f % args)))
```

定义此函数后,此评论中的所有示例都按预期工作。而且,由于这是一项“累积变更”(它是现有功能的严格子集),因此它是完全向后兼容的,并且可以(应该)包含在核心库中。

(另外,为了记录,`data2` 的示例是虚构的,但另一个示例在结构上与我为我的项目编写实际代码时使用的代码相同,我将使用可变参数的 `update-vals` 包装器。)
by
Alex Robbins:重新格式化 —— 你可以格式化问题,也可以格式化答案,但评论只是文本。
by
今天我正在赞扬 Clojure 中更新函数的多重参数签名,当这个函数不支持它时我感到很惊讶。下面是问题的具体情况

(defn scale [geometry factor]
  (update-vals geometry * factor))
by
我没有收到这些评论的电子邮件;也许 Alex Miller 也没有?我不希望对他进行垃圾邮件宣传,但我不知道这是否值得通过其他渠道提出。
by
我正在收到电子邮件(以及每天数十个其他问题的电子邮件,抱歉,但你是庞大群体中的一员 :))。如果这个请求获得足够的投票,我们将在未来考虑它。我们在这里定期基于投票数和我们在社区中看到的来审查问题。
...