【问题标题】:Applying a map to a function's rest argument将映射应用于函数的其余参数
【发布时间】:2013-05-02 20:39:58
【问题描述】:

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

(defn f [& r] ... )

我有一个序列 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))

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

【问题讨论】:

    标签: clojure


    【解决方案1】:

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

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

    【讨论】:

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

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

    你已经完成了。你已经做了你能做的一切。

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

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

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

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

    我发现,如果您想传递像:consider-nil true 这样的通用选项,您可能永远不会使用哈希映射{:consider-nil true} 调用该函数。

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

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

    【讨论】:

    • 你可能是对的。我发现它有时很有用,并认为它可以让您轻松构建相当灵活的功能,但我会确保给它一些额外的想法。至于我是如何陷入这种境地的,我试图在我的评论中对 Michiel Borkent 的回答做一个简短的解释——但不确定这是否有意义:)
    • 您提到您不想调用 h 并将结果传递给 g。即使不知道问题域,我也很难想象有充分的理由去做这样的事情。功能设计就是将大问题分解为独立的小问题。似乎您正在优化外观更好的函数调用。如果你能描述一下 g 和 h 完成的具体任务,我可能会做出更好的判断。
    • 我正在为我的团队正在开发的系统开发一个测试自动化框架,并且我正在尝试为其他人(他们对 Clojure 不太熟悉)设计一个简单的界面。对他们来说,将函数很好地组合是没有意义的,尤其是当 h 主要是作为 g 的辅助函数时。不过我确实同意你的观点,在考虑了更多之后,我做了一些重新设计,只是将面向外部的功能变成了其他功能的组合。向前迈出一步...谢谢!
    【解决方案3】:

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

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

    我确信有更有效的方法可以做到这一点,您是否希望最终陷入必须使用此类功能的情况还有待商榷,但它确实回答了原始问题。大多数情况下,我对这个名字很满意......

    【讨论】:

      【解决方案4】:

      我找到的最好的解决方案:

      (apply g (apply concat my-map))
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-04-17
        • 2012-02-28
        • 1970-01-01
        • 2019-05-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-08-07
        相关资源
        最近更新 更多