Clojure的Associative Destructuring允许通过数字索引解构一个 vector (可能还有一个 seq 或 list )。
到目前为止,clojure.org尚未提及此模式,但第二版的 Clojure的喜悦中提到了 。迈克尔·福格斯(Michael Fogus),克里斯·豪斯(Chris Houser),2014年5月,第14页。 59.这种方法出现在“关联解构”一节中-错误,因为这是基于索引的解构,这只是“关联解构”的特例,在上述书中,它被称为“带地图的解构”。 / p>
无论如何,结果出乎意料(Clojure 1.10.0):
在所有情况下,都提取索引0和3处的值。
这些工作符合预期:
(let [{firstthing 0, lastthing 3} [1 2 3 4]] [firstthing lastthing])
;=> [1 4]
(let [{firstthing 0, lastthing 3} (vec '(1 2 3 4))] [firstthing lastthing])
;=> [1 4]
但在列表上:
(let [{firstthing 0, lastthing 3} '(1 2 3 4)] [firstthing lastthing])
;=> [nil 4]
为什么位置0处有nil
?
类似地:
(let [{firstthing 0, lastthing 3} (seq '(1 2 3 4))] [firstthing lastthing])
;=> [nil 4]
但另一方面:
(let [{firstthing 0, lastthing 3} (vec (seq '(1 2 3 4)))] [firstthing lastthing])
;=> [1 4]
这是怎么回事?
附录:
(let [{firstthing 0, lastthing 3} { 1 2 3 4 } ] [firstthing lastthing])
;=> [nil 4]
...听起来很合理,因为要进行关联破坏的地图实际上是{1 2, 3 4}
。因此,不是通过 position 而是通过 integer键(可以改变脚下表达式的含义)进行查找的结果将精确地为{{1} }。不是矢量的东西会首先注入地图吗?
[nil 4]
看起来确实很像。...
(let [{firstthing 10, lastthing 30} (seq '(10 2 30 4))] [firstthing lastthing])
;=> [2 4]
哦,是的。
答案 0 :(得分:5)
为什么位置0处为零?
(let [{firstthing 0, lastthing 3} '(1 2 3 4)] [firstthing lastthing]) ;=> [nil 4]
如果您查看为该let
生成的代码:
user=> (macroexpand '(let [{firstthing 0, lastthing 3} '(1 2 3 4)] [firstthing lastthing]))
(let*
[map__8012 (quote (1 2 3 4))
map__8012 (if (clojure.core/seq? map__8012)
(clojure.lang.PersistentHashMap/create (clojure.core/seq map__8012))
map__8012)
firstthing (clojure.core/get map__8012 0)
lastthing (clojure.core/get map__8012 3)]
[firstthing lastthing])
您会看到,如果使用seq?
,它将转换为地图。所以:
user=> (def map__8012 (quote (1 2 3 4)))
#'user/map__8012
user=> (clojure.core/seq? map__8012)
true
user=> (clojure.lang.PersistentHashMap/create (clojure.core/seq map__8012))
{1 2, 3 4}
因此,对于密钥nil
,您将获得0
;对于密钥4
,您将获得3
。
答案 1 :(得分:1)
简短的答案是,只有地图和矢量是关联的。列表和序列不具有关联性。地图解构仅针对关联结构进行定义:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
(dotest
(let [mm {:a 1 :b 2}
vv [1 2 3]
ll (list 1 2 3)
sv (seq vv)
sl (seq ll) ]
(spyx (associative? mm))
(spyx (associative? vv))
(spyx (associative? ll))
(spyx (associative? sv))
(spyx (associative? sl)) ) )
结果:
(associative? mm) => true
(associative? vv) => true
(associative? ll) => false
(associative? sv) => false
(associative? sl) => false
Clojure经常采取(垃圾回收,垃圾回收)的态度(相当苛刻),而不是在调用带有无效参数的函数时抛出异常。
甚至还有a warning in ClojureDocs.org
我认为默认情况下,函数应更具防弹性(即检查arg值/类型),并且仅将精简版本提供为clojure.core.raw/get
或类似名称。
基于上面@cfrick的回答,我们看到了这种情况的起源。由于序列不是图谱,因此必须先将序列转换为图谱,然后才能进行解构。因此,let
假定您提供了一系列键值对,例如[k1 v1 k2 v2 ...]
,这些键值对应像以下那样转换为映射:
(apply hash-map [k1 v1 k2 v2 ...])
如果这不是您的意图,则应通过以下方式将序列显式转换为关联对象:
(zipmap (range) <the-seq>)
或更简单地说:
(vec <the seq>)
或者,您可以为clojure提供假定存在的键值序列:
(list 0 1 1 2 2 3 3 4)
请注意,如果您的序列具有(list 1 2 3)
之类的奇数长度,则在从seq到map的隐式转换过程中会出错。