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

欢迎!请查看关于页面以获取更多关于如何使用本页面的信息。

+6
集合
重新标记

updateswap! 等中的 & args 面部非常易于使用,并且组合效果很好,因此我很惊讶地得知 update-keysupdate-vals 没有这个,它们只是 [m f]

这是什么原因?

我认为将这些函数的 & args 添加到这些函数将更能与现有核心函数保持一致,并有助于避免不需要匿名函数。

1 个答案

+1

现有实用库(通常称为 map-keys / map-vals)中有许多与 update-keysupdate-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
由于实用库大多数情况下将它们视为序列函数,所以添加 `& args` 的arity无法工作,我认为这是其中一个原因。

我的具体情况是在从原子中清理一些记录后,完成了记录处理,但希望保留其余数据。类似于

```
(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` 包装器。)
Alex Robbins:重新格式化--你可以格式化问题和答案,但评论只是文本。
我在今天狂热地谈论 Clojure 更新函数的神奇的多变签名,当这个例子不支持它时我感到很惊讶。以下是需要考虑的情况

(defn scale [geometry factor]
  (update-vals geometry * factor))
我没有收到这些评论的电子邮件;也许 Alex Miller 也没有收到?我不想打扰他,但我想知道这是否值得通过另一个渠道提出。
我正在收到电子邮件(还有日常成百上千的其他问题,抱歉,你只是一大批人中的一个 :). 如果这个请求收到足够的投票,我们将在未来考虑。我们通常根据投票数和我们在社区中看到的状况来审查这里的问题。
...