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

欢迎!请查看 关于页面 获取更多此工作如何进行的详细信息。

+6
Collections
重新标记

updateswap!& args 非常符合运动学,并且组合得很好,所以我很惊讶地了解到 update-keysupdate-vals 没有,它们只是 `[m f]`。

原因是什么?

我认为向这些函数添加 & args 可以使函数与现有的核心函数更一致,并有助于避免不必要的匿名函数。

1 个答案

+1

现有实用库中有许多与 update-keysupdate-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`,这可能是其中一个原因。

我的特定用例是在原子结构中的嵌套记录清理时,我在完成记录后想要保留其余数据。

```
(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` 函数
```
(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
我收到电子邮件(还有许多其他日常问题,抱歉,您只是众多人中的一个:)。如果这个请求获得足够的投票,我们将在未来考虑它。我们不定期根据投票数和在社区中看到的内容,来审查这里的问题。
...