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

欢迎!请查看关于页面以了解更多有关操作的信息。

+6
集合
重新标记

在 `update`、`swap!` 等 JavaScript 函数中使用 `& 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中,所以这可能是一个可以查找的解决方案。

你在特定的场景下需要这样做吗?

你可以在以下链接中找到一些示例:

* 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

这些示例并不完全符合所需模式,但确实有几个符合。

编辑
由于实用库大多将这些处理为序列函数,所以arity不适用于添加`& args`,我认为这可能是原因之一。

我的特定用例是在 完成 归档 remembers 中的记录后清理嵌套结构中的某些记录,但想保留其他数据。例如

```
(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` 只接受两个参数。取而代之的是,你需要做的是
```
(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`包装器。)
Alex Robbins: 重新格式化 -- 您可以格式化问题和答案,但评论只是文本。
我最近在谈论Clojure中更新函数的令人惊叹的多参数签名,今天我对此感到惊讶,因为这个不支持。以下是问题案例

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