问题 将映射应用于函数的rest参数


在Clojure中,如果我有一个功能 F

(defn f [& r] ... )

我有一个seq ARGS 有了我想要调用f的参数,我可以轻松使用 应用

(apply f args)

现在,说我有另一个功能 G,它被设计为采用任意一些可选的命名参数 - 也就是说,其余参数被解构为一个映射:

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

我通常会做类似的事情

(g :a 1 :b 2)

但如果我碰巧有地图 我的地图 值{:a 1:b 2},我想“将”g“应用”到我的地图 - 换句话说,得到一些最终会成为上述调用的东西,然后我自然不能使用apply,因为它等同于

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

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

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

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

编辑: 建议的解决方案略有改进

(apply g (mapcat seq my-map))

至少删除了一个函数调用,但它可能仍然不是很清楚正在发生什么。


11717
2018-05-02 20:39


起源



答案:


我自己偶然发现了这个问题,最后定义了一个期望一个地图的函数。映射可以具有可变数量的键/值对,并且如果足够灵活,则不需要&rest参数。申请也没有痛苦。让生活更轻松!

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

6
2018-05-02 20:46



谢谢!这当然是一种方式,但是关联休息解构所允许的可选的命名参数总是需要放在函数调用中的一个映射中 - 特别是,如果你不想使用任何参数,那么' d必须做(g {})或(g nil)。我现在看到我在问题中还不清楚 - 我将编辑以澄清g被设计为采用可选的命名参数,并且问题是如果你碰巧将你的参数包含在地图中,如何习惯性地调用它。 - Marxama
您还可以使用多个arities,一个用于零参数,一个用于一个参数: (defn g ([] ...) ([{:keys [a b] :as m}] ...)) - Michiel Borkent
谢谢!最后我选择了这个解决方案 - 我在这种情况下最终得到的原因是原始函数变得越来越大,使用可选参数的部分非常适合分解为它自己的函数h。我仍然希望以同样的方式调用g,然后在g调用h中将可选参数分组到一个映射中,而不是必须调用h并将结果传递给g。在这个特殊的情况下,你的建议很好,但我写了一个简单的函数,它适用于原始问题 - 将它作为答案发布。 - Marxama


答案:


我自己偶然发现了这个问题,最后定义了一个期望一个地图的函数。映射可以具有可变数量的键/值对,并且如果足够灵活,则不需要&rest参数。申请也没有痛苦。让生活更轻松!

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

6
2018-05-02 20:46



谢谢!这当然是一种方式,但是关联休息解构所允许的可选的命名参数总是需要放在函数调用中的一个映射中 - 特别是,如果你不想使用任何参数,那么' d必须做(g {})或(g nil)。我现在看到我在问题中还不清楚 - 我将编辑以澄清g被设计为采用可选的命名参数,并且问题是如果你碰巧将你的参数包含在地图中,如何习惯性地调用它。 - Marxama
您还可以使用多个arities,一个用于零参数,一个用于一个参数: (defn g ([] ...) ([{:keys [a b] :as m}] ...)) - Michiel Borkent
谢谢!最后我选择了这个解决方案 - 我在这种情况下最终得到的原因是原始函数变得越来越大,使用可选参数的部分非常适合分解为它自己的函数h。我仍然希望以同样的方式调用g,然后在g调用h中将可选参数分组到一个映射中,而不是必须调用h并将结果传递给g。在这个特殊的情况下,你的建议很好,但我写了一个简单的函数,它适用于原始问题 - 将它作为答案发布。 - Marxama


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

你完成了。你已尽力而为。

使用Common Lisp风格并不是真正的烦恼:关键字arg函数。如果你环顾Clojure代码,你会发现几乎没有任何函数以这种方式编写。

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

“我不太喜欢的一件事是关键词参数(8)。它们对我来说似乎并不太可能;我有时会这样做,但我最小化了这样做的时间。” (资源

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

我发现在你要传递一般选项的情况下 :consider-nil true 您可能永远不会使用哈希映射调用该函数 {:consider-nil true}

在您想要根据哈希映射的某些键进行评估的情况下,您有99%的时间都有 f ([m & args]) 宣言。

当我开始在Clojure中定义函数时,我遇到了同样的问题。但是在仔细考虑了我试图解决的问题之后,我注意到自己在函数声明中几乎从不使用析构函数。


3
2018-05-04 05:43



你可能是对的。我发现它有时很有用,并认为它可以让你轻松地构建相当灵活的功能,但我会确保给它一些额外的想法。至于我是如何在这种情况下结束的,我试着在我对Michiel Borkent的回答中给出一个简短的解释 - 不确定它是否有任何意义:) - Marxama
你提到你不想调用h并将结果传递给g。即使不知道问题领域,我也很难想象做出这样的事情的充分理由。功能设计就是将大问题转化为独立的小问题。看起来你正在优化一个更好看的函数调用。如果你要描述g和h所完成的具体任务,我可以回答更好的判断。 - Leon Grapenthin
我正在为我的团队正在研究的系统开发一个测试自动化框架,我正在尝试为其他人(他们对Clojure不是很有经验)制定一个简单的界面。对于他们来说,将函数很好地组合是没有意义的,特别是因为h主要是作为g的辅助函数。不过我确实同意你的看法,经过多次思考后,我做了一些重新设计,并简单地将外部功能与其他功能组合在一起。向前迈出一步......谢谢! - Marxama


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

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

我确信有更有效的方法可以做到这一点,并且你是否想要在你必须使用这样一个功能的情况下结束辩论,但它确实回答了原来的问题。大多数情况下,我对孩子的名字感到满足......


1
2018-05-08 20:54





我发现最好的解决方案:

(apply g (apply concat my-map))

0
2017-10-15 14:51