从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经验,所以随着时间的推移它会变得更好)。
所以我想问,是否有一种基本理由使用一种风格而不是另一种风格?例如,编译器是否生成字节码,在第二种情况下效率更高。或者我还应该注意其他原因吗?
答案 0 :(得分:4)
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
解构)。
答案 1 :(得分:3)
: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]
...)
VS
(let [id (:id person)
name (:name person)
address (:address person)
age (:age person)
salary (:salary person)]
...)
答案 2 :(得分:1)
为了完整起见,这是来自Clojure Library Coding Standards:
惯用语代码经常使用解构。但是,如果要将子结构作为调用者合同的一部分进行通信,则只应在arg列表中进行解构。否则,在第一行中进行结构化。示例:本书中的my snake code未通过此测试,在arg列表中进行了太多的解构。