简体   繁体   English

为什么可以将键值对传递给破坏地图的函数?

[英]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 & gathers any arguments after it into a collection &将其后面的任何参数收集到一个集合中
  • The map destructuring form then takes the collection, makes a map out of it if required and binds the names to the keys listed in the vector. 然后,地图解构表单获取集合, 需要时从中生成一个映射,并将名称绑定到向量中列出的键。

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.

相关问题 有没有办法编写一个 clojure 函数来解构地图并返回地图的任何部分? - is there a way to write a clojure function that destructures a map and returns any portion of the map? 为什么Clojure向量用于传递键值对? - Why are Clojure vectors used to pass key-value pairs? 如何将 hashmap 的所有键值对传递到 function (不传递整个哈希图)? - How to pass all key value pairs of a hashmap into a function (without passing the whole hashmap)? Clojure习惯用语:理智地传递函数值对 - Clojure idioms: sanely pass function-value pairs 如何在不知道Clojure中的键的情况下将地图构造成键值对? - How do you destructure a map into key-value pairs without knowing the keys in Clojure? 如何在Clojure中使用值作为谓词从键-值对列表中创建映射? - How to create a map from a list of key-value pairs using values as a predicate in Clojure? 为什么地图关键字无法检索在地图中可以清楚看到的值? - Why does the map key not retrieve the value that can be plainly seen in the map? 将地图中的函数基于其键应用于地图中的每个值 - applying function in a map to each value in a map based on its key 将嵌套映射分解为键值对 - Decompose nested maps into key-value pairs 解析键值对以便在clojure中进行排序 - Parsing key-value pairs for sorting in clojure
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM