11

在 Clojure 中,如果我有一个函数f

(defn f [& r] ... )

我有一个 seq args和我想调用 f 的参数,我可以很容易地使用apply

(apply f args)

现在,假设我有另一个函数g,它被设计为采用许多可选的命名参数中的任何一个 - 也就是说,其余参数被解构为映射:

(defn g [& {:keys [a b] :as m}] ... )

我通常会通过执行类似的操作来调用 g

(g :a 1 :b 2)

但是如果我碰巧有一个值为 {:a 1 :b 2} 的地图my-map,并且我想将 g“应用”到 my-map - 换句话说,得到一些最终会作为上述调用的东西,那么我自然不能使用 apply,因为它相当于

(g [:a 1] [:b 2])

有没有很好的方法来处理这个?我可以在我的设计中偏离轨道以结束这个吗?我能找到的最好的解决方案是

(apply g (flatten (seq my-map)))

但我肯定不喜欢它。有更好的解决方案吗?

编辑:对建议的解决方案的轻微改进可能是

(apply g (mapcat seq my-map))

这至少删除了一个函数调用,但可能仍然不太清楚发生了什么。

4

4 回答 4

6

我自己偶然发现了这个问题,并最终定义了期望一张地图的函数。一个映射可以有可变数量的键/值对,如果足够灵活,那么就不需要 & rest 参数。应用也没有痛苦。让生活轻松很多!

(defn g [{:keys [a b] :as m}] ... )
于 2013-05-02T20:46:12.640 回答
3

没有比转换为 seq 更好的直接方法了。

你完成了。你已经尽力了。

拥有 Common Lisp 风格的 :keyword arg 函数并不是真正的 clojurish。如果您查看 Clojure 代码,您会发现几乎没有函数是这样编写的。

即使是伟大的 RMS 也不是他们的粉丝:

“我非常不喜欢的一件事是关键字参数 (8)。它们对我来说似乎不太 Lispy;我有时会这样做,但我会尽量减少这样做的时间。” (来源

在您必须将完整的哈希映射分解为多个部分以将所有这些映射作为关键字映射参数传递的那一刻,您应该质疑您的函数设计。

我发现在您想要传递一般选项的情况下,您:consider-nil true可能永远不会使用 hash-map 调用该函数{:consider-nil true}

如果您想根据哈希映射的某些键进行评估,则 99% 的时间都有f ([m & args])声明。

当我开始在 Clojure 中定义函数时,我遇到了同样的问题。然而,在更多地考虑了我试图解决的问题之后,我注意到自己几乎从未在函数声明中使用析构。

于 2013-05-04T05:43:36.100 回答
1

这是一个非常简单的函数,可以完全按照 apply 的方式使用,除了最终的 arg(应该是一个映射)将扩展为 :key1 val1 :key2 val2 等。

(defn mapply
  [f & args]
  (apply f (reduce concat (butlast args) (last args))))

I'm sure there are more efficient ways to do it, and whether or not you'd want to end up in a situation where you'd have to use such a function is up for debate, but it does answer the original question. Mostly, I'm childishly satisfied with the name...

于 2013-05-08T20:54:36.273 回答
0

我发现的最好的解决方案:

(apply g (apply concat my-map))
于 2017-10-15T14:51:08.520 回答