在Clojure中,如果我有一个函数 f ,
(defn f [& r] ... )
我有一个seq args ,带有我想要调用f的参数,我可以轻松使用 apply :
(apply f args)
现在,假设我有另一个函数 g ,它被设计为接受任意一些可选的命名参数 - 也就是说,其余参数被解构为map:
(defn g [& {:keys [a b] :as m}] ... )
我通常会通过执行类似
的操作来调用g(g :a 1 :b 2)
但是如果我碰巧有一个地图 my-map ,其值为{:a 1:b 2},我想将“g”应用于我的地图 - 换句话说,得到一些最终会成为上述调用的东西,然后我自然不能使用apply,因为它等同于
(g [:a 1] [:b 2])
有没有一个很好的方法来处理这个?我的设计可能会偏离轨道吗?我能找到的最佳解决方案是
(apply g (flatten (seq my-map)))
但我肯定不喜欢它。有更好的解决方案吗?
编辑:建议的解决方案略有改进可能
(apply g (mapcat seq my-map))
至少删除了一个函数调用,但可能仍然不太清楚正在发生什么。
答案 0 :(得分:6)
我自己偶然发现了这个问题,并最终确定了期望一张地图的功能。地图可以具有可变数量的键/值对,并且如果足够灵活,则不需要&休息论点。申请也没有痛苦。让生活更轻松!
(defn g [{:keys [a b] :as m}] ... )
答案 1 :(得分:3)
没有比转换为seq更好的直接方式。
你完成了。你已尽力而为。
使用Common Lisp风格并不是真的很奇怪:关键字arg函数。如果你环顾Clojure代码,你会发现几乎没有任何函数以这种方式编写。
即使是伟大的RMS也不是他们的粉丝:
“我不太喜欢的一件事是关键词参数(8)。它们对我来说似乎不太相似;我有时候会这样做,但是当我这样做的时候,我会最小化。” (Source)
当你必须将一个完整的哈希映射分解成碎片只是为了将它们全部作为关键字映射参数传递时,你应该质疑你的功能设计。
我发现在你希望传递像:consider-nil true
这样的常规选项的情况下,你可能永远不会使用哈希映射{:consider-nil true}
来调用该函数。
如果您想根据哈希映射的某些键进行评估,那么99%的时间都会有f ([m & args])
声明。
当我开始在Clojure中定义函数时,我遇到了同样的问题。但是在仔细考虑了我试图解决的问题之后,我发现自己几乎从未在函数声明中使用析构函数。
答案 2 :(得分:1)
这是一个非常简单的函数,可以完全用作apply,除了最终的arg(应该是一个map)将扩展为:key1 val1:key2 val2等。
(defn mapply
[f & args]
(apply f (reduce concat (butlast args) (last args))))
我确信有更有效的方法可以做到这一点,以及你是否想要在你必须使用这样一个功能的情况下结束是有争议的,但它确实回答了原始问题。大多数情况下,我对孩子的名字很满意......
答案 3 :(得分:0)
我发现最好的解决方案:
(apply g (apply concat my-map))