Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Clojure 为什么可以将键值对传递给分解映射的函数?_Clojure_Destructuring - Fatal编程技术网

Clojure 为什么可以将键值对传递给分解映射的函数?

Clojure 为什么可以将键值对传递给分解映射的函数?,clojure,destructuring,Clojure,Destructuring,我以为我理解解构,但我在读clojure的博客,这让我很困惑。如果您编写了一个函数,如下所示: (defn f [& {:keys [foo bar]}] (println foo " " bar)) 为什么你可以这样称呼它: (f :foo 1 :bar 2) (f {:foo 1 :bar 2}) IllegalArgumentException No value supplied for key: {:foo 1, :bar 2} clojure.lang.Persis

我以为我理解解构,但我在读clojure的博客,这让我很困惑。如果您编写了一个函数,如下所示:

(defn f [& {:keys [foo bar]}] 
  (println foo " " bar))
为什么你可以这样称呼它:

(f :foo 1 :bar 2)
(f {:foo 1 :bar 2})
IllegalArgumentException No value supplied for key: {:foo 1, :bar 2}  clojure.lang.PersistentHashMap.createWithCheck (PersistentHashMap.java:89)
我的第一个想法是我的函数应该这样调用:

(f :foo 1 :bar 2)
(f {:foo 1 :bar 2})
IllegalArgumentException No value supplied for key: {:foo 1, :bar 2}  clojure.lang.PersistentHashMap.createWithCheck (PersistentHashMap.java:89)
但很明显,这是行不通的。我认为这与
&
的工作方式有关。但我一直认为它后面的东西是一个向量,因此你必须像向量一样分解它后面的任何东西


有人能向我解释一下这个定义是如何运作的吗?谢谢

按顺序执行&和分解表单:

  • &将其后面的所有参数收集到集合中
  • 然后,map destructuring表单获取集合,根据需要从中生成一个映射,并将名称绑定到向量中列出的键

map destructuring表单中的向量只是用于构建destructuring/binding的语法,除了输入表单之外,没有任何含义

如果没有&在defn中,第二种形式有效,而第一种形式无效。

使用&第一个表单有效,第二个表单无效。

您可以通过手动调用
destructure
查看封面下的内容。让我们从一个简单的例子开始:

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)]
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)]
这对应于
(let[{foo:foo}{:foo 42}]…)
(正如您可以通过
(macroexpand-1'(let[{foo:foo}{:foo 42}]…)
验证的那样)。输出的第二行是重要的位。映射绑定形式可以通过两种方式工作:如果绑定的值是seq,则seq将“倒”到哈希映射中(就像通过
(将哈希映射应用于seq)
。否则,将假定该值为关联值并直接使用。添加了seq“倾倒”功能

让我们测试一下:

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
在第一种情况下,值不是seq,因此直接使用它。在第二种情况下,列表是seq,因此在绑定到
{foo:foo}
之前将其“倒”到哈希映射中。第三种情况表明,这种倒在语义上等同于
(应用哈希映射seq)

现在让我们看一下类似于您的示例的内容:

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)]
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)]
nthnext
位来自
&
——在这种情况下,因为在
&
之前没有固定的参数,所以我们有一个
(nthnext vec#0)
,这相当于将
args
转换成一个seq(如有必要)。然后我们进行如上所述的映射分解。因为
&
保证我们有一个seq,所以映射分解的seq特例将始终被触发,并且在绑定到映射表单之前,参数将始终“倒”到哈希映射中

如果此示例与原始fn之间的关系不清楚,请考虑:

user> (macroexpand-1 '(fn [& {:keys [foo bar]}]))
(fn* ([& p__26214] (clojure.core/let [{:keys [foo bar]} p__26214])))

“地图解构形式中的向量只是语法”——事实上,列表在这里也同样适用。