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
编辑 by
由于实用库大多将这些视为序列函数,因此arity 对于添加 `& 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`只接受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`函数来包装现有的一个
```
(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 也一样?我不想打扰他,但我想知道这是否值得通过其他渠道提出。
我会收到电子邮件(和每天成千上万个其他问题的电子邮件,抱歉,你只是庞大人群中的一员 :)。如果这个请求获得足够的投票,我们将在未来考虑它。我们定期根据投票计数和我们在社区中看到的情况来审查这里的问题。
...