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]