Clojure 解构分为两类:顺序解构和关联解构。顺序解构将一个顺序数据结构表示为 let
绑定中的 Clojure 向量。
这种类型的解构可用于任何可以以线性时间遍历的数据结构,包括列表、向量、序列、字符串、数组以及支持 nth
的任何数据结构。
(def my-vector [1 2 3])
(def my-list '(1 2 3))
(def my-string "abc")
;= It should come as no surprise that this will print out 1 2 3
(let [[x y z] my-vector]
(println x y z))
;= 1 2 3
;= We can also use a similar technique to destructure a list
(let [[x y z] my-list]
(println x y z))
;= 1 2 3
;= For strings, the elements are destructured by character.
(let [[x y z] my-string]
(println x y z)
(map type [x y z]))
;= a b c
;= (java.lang.Character java.lang.Character java.lang.Character)
顺序解构的关键是,你将值逐个绑定到向量中的符号。例如,向量 [x y z]
将逐个与列表 '(1 2 3)
中的每个元素匹配。
在某些情况下,你正在解构的集合的大小与解构绑定的大小并不完全相同。如果向量太小,额外的符号将被绑定到 nil。
(def small-list '(1 2 3))
(let [[a b c d e f g] small-list]
(println a b c d e f g))
;= 1 2 3 nil nil nil nil
(def large-list '(1 2 3 4 5 6 7 8 9 10))
(let [[a b c] large-list]
(println a b c))
;= 1 2 3
解构使你能够完全控制你选择绑定(或不绑定)的元素以及如何绑定它们。
很多时候,你并不需要访问集合中的所有元素,只需要访问其中的一些元素。
(def names ["Michael" "Amber" "Aaron" "Nick" "Earl" "Joe"])
假设你想在一行中打印第一个元素,在另一行中打印剩余的元素。
(let [[item1 item2 item3 item4 item5 item6] names]
(println item1)
(println item2 item3 item4 item5 item6))
;= Michael
;= Amber Aaron Nick Earl Joe
此绑定有效,但即使使用解构,它也相当笨拙。相反,我们可以使用 &
将尾部元素组合成一个序列。
(let [[item1 & remaining] names]
(println item1)
(apply println remaining))
;= Michael
;= Amber Aaron Nick Earl Joe
你可以通过将不打算使用的绑定绑定到任何你选择的符号来忽略它们。
(let [[item1 _ item3 _ item5 _] names]
(println "Odd names:" item1 item3 item5))
;= Odd names: Michael Aaron Earl
你可以使用 :as all
将整个向量绑定到符号 all
。
(let [[item1 :as all] names]
(println "The first name from" all "is" item1))
;= The first name from [Michael Amber Aaron Nick Earl Joe] is Michael
让我们停下来,进一步了解 :as
和 &
的类型。
(def numbers [1 2 3 4 5])
(let [[x & remaining :as all] numbers]
(apply prn [remaining all]))
;= (2 3 4 5) [1 2 3 4 5]
这里 remaining
被绑定到一个包含 numbers
向量中剩余元素的序列,而 all
被绑定到原始 vector
。当我们解构字符串时会发生什么?
(def word "Clojure")
(let [[x & remaining :as all] word]
(apply prn [x remaining all]))
;= \C (\l \o \j \u \r \e) "Clojure"
这里 all
被绑定到原始结构(字符串、向量、列表,无论是什么),x
被绑定到字符 \C
,remaining
是字符的剩余列表。
(def fruits ["apple" "orange" "strawberry" "peach" "pear" "lemon"])
(let [[item1 _ item3 & remaining :as all-fruits] fruits]
(println "The first and third fruits are" item1 "and" item3)
(println "These were taken from" all-fruits)
(println "The fruits after them are" remaining))
;= The first and third fruits are apple and strawberry
;= These were taken from [apple orange strawberry peach pear lemon]
;= The fruits after them are (peach pear lemon)
解构也可以嵌套,以访问任意级别的顺序结构。让我们回到我们一开始的向量 my-line
。
(def my-line [[5 10] [10 20]])
(let [[[x1 y1][x2 y2]] my-line]
(println "Line from (" x1 "," y1 ") to (" x2 ", " y2 ")"))
;= "Line from ( 5 , 10 ) to ( 10 , 20 )"
当你有嵌套的向量时,你也可以在任何级别使用 :as
或 &
。
(let [[[a b :as group1] [c d :as group2]] my-line]
(println a b group1)
(println c d group2))
;= 5 10 [5 10]
;= 10 20 [10 20]