Clojure 2024 状态调查! 分享您的想法。

欢迎!请查看 关于 页面了解更多此网站的运作方式。

+6
Collections
重标记

`update`、`swap!` 等中的 & args 参数非常便于使用,并且能够很好地组合,所以当我了解到 `update-keys` 和 `update-vals` 不具备这个特性,它们只是 [m f] 时,我感到很惊讶。

这是什么原因呢?

我认为将这些函数的 &am

1 答案

+1

现有实用库(通常称为 map-keys / map-vals)中存在一些类似于 update-keysupdate-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

并非所有这些都符合所需的模式,但确实有一些是这样。

编辑了
由于实用库大多将这些视为类似序列函数,所以通過添加`& args`来实现arity不适用,这想像是其中一个原因。

我的具体情况是在清理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" {}}
````

编辑了
[编辑:在这个网站上如何正确格式化代码?我试了,但没有弄明白,所以我就以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`的`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
我正在收到电子邮件(以及每天数十个其他问题,抱歉,你只是众多中的一员 :)。如果这个请求获得足够的投票,我们将考虑它在未来的可能性。我们定期根据投票数量和我们在社区中看到的情况审查这里的问题。
...