【问题标题】:Destructuring map argument with :keys vs old fashioned let使用 :keys 与老式的 let 解构 map 参数
【发布时间】:2016-06-19 03:18:46
【问题描述】:

从Scheme背景学习Clojure,我的直觉是编写如下代码:

(defn my-func [data]
  (let [right (:right data)
        left  (:left data)]
     ... body ... ))

但是,我很快就被告知模式匹配是 Clojure(或者实际上是 Haskell 和 Scala)的一大特点,所以我觉得有必要写:

(defn my-func [ {:keys [right left] } ]
   ... body ...)

所以我的代码现在充斥着这种类型的语法。不知怎的,我不喜欢它。我发现我的代码看起来不那么吸引人,甚至可能更不可读(但这可能是由于缺乏惯用的 Clojure 经验,所以随着时间的推移它会变得更好)。

所以我想问,使用一种风格而不是另一种风格有根本原因吗?例如,编译器是否生成字节码,这在第二种情况下效率更高。或者还有其他我应该注意的原因吗?

【问题讨论】:

    标签: clojure


    【解决方案1】:

    Destructuring is not pattern matching!当你写这个时:

    (defn my-func [{:keys [right left]}]
      ,,,)
    

    和这个是一样的:

    (defn my-func [data]
      (let [{:keys [right left]} data]
        ,,,))
    

    与此相同:

    (defn my-func [data]
      (let [right (:right data)
            left (:left data)]
        ,,,))
    

    As Piotrek pointed out,使用更简洁的形式没有性能优势。不过,还有一个好处:简洁。

    As Alan pointed out:keys 解构的主要优点是每个键名只需写一次而不是两次。

    As you pointed out,直接在函数的参数向量中进行解构会降低代码的可读性,这就是为什么它在 Clojure 中实际上并不常见。

    很棒的是,由于在 Clojure 中解构不与参数命名一起完成,因此您可以两全其美(我在上面给出的第二种形式):

    (defn my-func [data]
      (let [{:keys [right left]} data]
        ,,,))
    

    这样,您可以获得可读性(特别是如果您将参数命名为比 data 更好的名称)和简洁(通过使用 :keys 解构)。

    【讨论】:

    • 非常感谢山姆!也感谢关于解构 v 模式匹配的参考
    【解决方案2】:

    :keys 解构只是一种语法糖,实际上扩展为对get 的一系列调用和对符号的赋值。例如:

    (macroexpand-1
      '(let [{:keys [a b]} {:a 1 :b 2}]))
    

    扩展为:

    (let*
     [map__2507
      {:a 1, :b 2}
      map__2507
      (if (clojure.core/seq? map__2507) (clojure.lang.PersistentHashMap/create (clojure.core/seq map__2507)) map__2507)
      a
      (clojure.core/get map__2507 :a)
      b
      (clojure.core/get map__2507 :b)])
    

    解构的主要目标(注意它只是解构 - 不是完全模式匹配)是简洁的代码,不涉及性能优化。

    为了说明没有:keys 解构的代码如何更冗长,请看示例:

    (let [{:keys [id name address age salary]} person]
      ...)
    

    (let [id (:id person)
          name (:name person)
          address (:address person)
          age (:age person)
          salary (:salary person)]
      ...)
    

    【讨论】:

    • 正如 Piotrek 提到的,最大的好处是您不需要重复每个名称两次(例如符号“right”和关键字“:right”),并且符号“data”从每行以及 arg 列表。
    • 非常感谢 Piotrek/Alan
    【解决方案3】:

    为了完整起见,这是来自 Clojure Library Coding Standards

    惯用代码大量使用解构。但是,如果您想将子结构作为调用者合同的一部分进行通信,则只应在 arg 列表中进行解构。否则,在第一行 let 中解构。示例:书中的my snake code 未通过此测试,在 arg 列表中进行了太多解构。

    【讨论】:

    • 非常感谢瓦伦丁!
    猜你喜欢
    • 1970-01-01
    • 2020-10-02
    • 2016-05-31
    • 1970-01-01
    • 2021-04-07
    • 1970-01-01
    • 1970-01-01
    • 2014-10-26
    • 2019-02-22
    相关资源
    最近更新 更多