2024 Clojure 状况调查!中分享你的观点。

欢迎!请参阅 关于页面以了解有关这是如何工作的更多信息。

+6
集合
重标记

`update`、`swap!` 等中的 `& args` 非常符合人体工程学,组合非常好,所以我感到惊讶的是,`update-keys` 和 `update-vals` 没有这个,它们只是 `[m f]`。

这是为什么?

我认为将这些函数中的 `& args` 添加到更符合现有核心函数,并有助于避免不必要的匿名函数。

1 个答案

+1

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

有一个不同点是,update 等方法只对一个值调用函数 f,而 update-keysupdate-vals 方法则对多个值调用函数 f。我不确定您有多频繁地希望将相同的静态参数(用于每个键或值)传递给应用的函数。我没有具体的示例(但也许这仅仅是因为现有的函数目前不会这样做)。

当然,可以通过 partial 或匿名函数将这个操作推入 f,所以这可能是可以搜索的内容。

您有特定的使用场景需要这个功能吗?

by
您可以在下列示例中找到一些

* 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

并非全部都符合所需的模式,但确实有些是这样。
by
编辑
由于实用工具库大多将这些视为序列函数,因此 arity 不会用于添加 `& args`,我认为这是原因之一。

我的特定用例是在清理一个原子的嵌套结构中的记录,当我完成记录时,但想要保留其余数据。类似下面的代码:

```
(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-vals` 可能比 `update-keys` 更有用,但对于一个虚构的示例,你可能想为映射中的所有键后缀添加某些内容。

```
(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` 只接受2个参数。相反,你需要做点像这样的事情
```
(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` 函数的 `update-vals` 函数
```
(defn update-vals [m f & args]
  `(clojure.core/update-vals m #(apply f % args)))
```

定义好之后,本评论中所有的示例都按预期工作。由于这是一个“增量更改”(它是现有功能的严格超集),它完全向后兼容,并且可以(也应该)包含在核心库中。

(顺便说一句,"data2" 示例是虚构的,但另一个示例与我为我的项目刚刚编写的实际代码在结构上完全相同,我将使用可变参数的 `update-vals` 包装器。)
Alex Robbins:重新格式化--您可以格式化问题和答案,但注释只是文本。
我今天在夸赞 Clojure 中 update 函数的神奇多态签名,惊讶地发现这个不支持它。以下是相关案例

(defn scale [geometry factor]
  (update-vals geometry * factor))
我没有收到这些评论的邮件;也许 Alex Miller 也收不到?我不想烦他,但我想知道这是否值得通过其他渠道提出。
我收到了邮件(以及每天关于数十个其他问题的邮件,对此表示歉意,您只是众多人中的一位)。如果这个请求获得足够的投票,我们将在未来考虑它。我们通常根据投票数以及我们在社区中看到的情况,定期审查这些问题。
...