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-keys` 和 `update-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-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`包装器。)
by
Alex Robbins: 重新格式化——你可以格式化问题和回答,但评论只是文本。
» by
我今天在赞美 Clojure 中更新函数的神奇的多个参数签名时,惊奇地发现这个不支持。以下是需要考虑的情况

(defn scale [geometry factor]
  (update-vals geometry * factor))
by
我并没有收到这些评论的邮件;Alex Miller 是否也没有收到?我不想打扰他,但我想知道这是否值得通过其他渠道提出。
by
我收到了邮件(以及每天成百上千的其他问题的邮件,抱歉你只是庞大群体中的一员 :). 如果这个请求收到足够的投票,我们将考虑它在未来的情况。我们定期根据投票数量和我们所看到的社区情况审查这里的问题。
...