[英]Why is it possible to pass in key value pairs to a function that destructures a map?
I thought I understood destructuring, but I was reading a clojure blog and this confused me. 我以为我理解了解构,但我正在阅读一个clojure博客,这让我很困惑。 If you have a function written like:
如果你有一个像这样的函数:
(defn f [& {:keys [foo bar]}]
(println foo " " bar))
Why can you call it like this: 你为什么这样称呼它:
(f :foo 1 :bar 2)
My first thought was that my function was supposed to be called like this: 我的第一个想法是我的函数应该像这样调用:
(f {:foo 1 :bar 2})
IllegalArgumentException No value supplied for key: {:foo 1, :bar 2} clojure.lang.PersistentHashMap.createWithCheck (PersistentHashMap.java:89)
But obviously that doesn't work. 但显然这不起作用。 I think this has something to do with the way
&
works. 我认为这与方式
&
工作有关。 But I always thought that the thing after it is a vector and therefore you'd have to destructure anything after it like a vector. 但是我一直认为它之后的东西是一个向量,因此你必须在它之后像向量一样去构造任何东西。
Can someone explain to me how/why this definition works the way it does? 有人可以向我解释这个定义是如何/为什么以它的方式工作的? Thanks
谢谢
The & and destructuring form work sequentially: &和解构表单按顺序工作:
The vector in the map destructuring form is just syntax used to build the desctructuring/binding and does not imply anything aobut the input form 地图解构形式中的向量只是用于构建解构/绑定的语法,并不意味着任何输入形式的输出形式
The without the & in the defn the second form will work and the first will not. 如果没有&在defn中第二种形式将起作用,而第一种形式则不起作用。
With the & the first form will work and the second will not. 随着&第一种形式将起作用,第二种形式将不起作用。
You can see what's going on under the covers by calling destructure
manually. 您可以通过手动调用
destructure
来查看正在进行的操作。 Let's start with a simpler example: 让我们从一个更简单的例子开始:
user> (destructure ['{foo :foo} {:foo 42}])
[map__26147 {:foo 42}
map__26147 (if (clojure.core/seq? map__26147)
(clojure.lang.PersistentHashMap/create
(clojure.core/seq map__26147))
map__26147)
foo (clojure.core/get map__26147 :foo)]
This corresponds to (let [{foo :foo} {:foo 42}] ...)
(as you can verify with (macroexpand-1 '(let [{foo :foo} {:foo 42}] ...))
. The second line of the output is the important bit. A map binding form can work in two ways: if the value being bound is a seq, the seq will be 'poured' into a hash-map (as if by (apply hash-map the-seq)
. Otherwise, the value is assumed to be an associative and used directly. The seq 'pouring' feature was added in this commit . 这对应于
(let [{foo :foo} {:foo 42}] ...)
(你可以验证(macroexpand-1 '(let [{foo :foo} {:foo 42}] ...))
,输出的第二行是重要比特的地图结合形式可以以两种方式工作:如果约束的值是SEQ,带seq将“倾”为哈希映射(如如果由(apply hash-map the-seq)
。否则,该值被假定为关联并直接使用。在此提交中添加了seq“pouring”功能。
Let's test this out: 我们来测试一下:
user> (let [{foo :foo} {:foo 42}] foo)
42
user> (let [{foo :foo} (list :foo 42)] foo)
42
user> (let [{foo :foo} (apply hash-map (list :foo 42))] foo)
42
In the first case, the value is not a seq, so it's used directly. 在第一种情况下,该值不是seq,因此直接使用。 In the second case, a list is a seq, so it is 'poured' into a hash-map before being bound to
{foo :foo}
. 在第二种情况下,列表是seq,因此在绑定到
{foo :foo}
之前将其“倾倒”到哈希映射中。 The third case shows that this pouring is semantically equivalent to (apply hash-map the-seq)
. 第三种情况表明,这种倾注在语义上等同于
(apply hash-map the-seq)
。
Now let's look at something like your example: 现在让我们看看你的例子:
user> (destructure '[[& {:keys [foo bar]}] args])
[vec__26204 args
map__26205 (clojure.core/nthnext vec__26204 0)
map__26205 (if (clojure.core/seq? map__26205)
(clojure.lang.PersistentHashMap/create
(clojure.core/seq map__26205))
map__26205)
bar (clojure.core/get map__26205 :bar)
foo (clojure.core/get map__26205 :foo)]
The nthnext
bit is from the &
— in this case, because there are no fixed parameters before the &
, we have an (nthnext vec# 0)
, which amounts to just converting args
into a seq (if necessary). nthnext
位来自&
- 在这种情况下,因为在&
之前没有固定参数,我们有一个(nthnext vec# 0)
,这相当于只将args
转换为seq(如果需要)。 Then we have the map destructuring as above. 然后我们如上所述进行地图解构。 Because the
&
guarantees we have a seq, the seq special case for map destructuring will always be triggered, and the args will always be 'poured' into a hash-map before being bound to the map form. 因为
&
保证我们有seq,所以seq特殊情况下的地图解构将始终被触发,并且args将始终被“倾注”到哈希映射中,然后绑定到地图表单。
In case the relationship between this example and your original fn is not clear, consider: 如果此示例与原始fn之间的关系不明确,请考虑:
user> (macroexpand-1 '(fn [& {:keys [foo bar]}]))
(fn* ([& p__26214] (clojure.core/let [{:keys [foo bar]} p__26214])))
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.