標籤:
Clojure提供了幾種強大的資料結構(集合)
一、集合種類
1、vector
相當於數組,如: [2 3 5] , ["ad" "adas" "adadsads"]
2、list
相當於鏈表,如: ‘(2 3 5) , ‘("ad" "adas" "adadsads")
與vector相比,[]變成了() ,又因為 ()符號是用於函數調用,為了區別,需要在 ( 前面加上 一個單引號‘
3、map
文法格式如: {:a 1 :b 1}
map是1個或多個 key-value對,其中key標識符前面要求有:標識 。map的key本身就是函數,通過它可以尋找它所對應的值。
如:
user=> (def data {:a 1 :b 2} )user=> (:a data)1user=> (:a {:a 1 :b 2} )1user=> (:a {:a 3 :b 2} )3
上面第一個語句定義了一個值data,是個map。 第二個語句是擷取關鍵字:a的值,這裡:a就是一個函數。
最後兩個語句就是直接從map字面量上查詢key對應的值。
4、set
文法格式如: #{值1 值2 ....}
user=> #{1 2 3}
#{1 3 2}
說明,相比java的集合,Clojure的集合對象有如下幾個特徵:
1) immutable 集合的內容在初始化後是不可修改的,後續對它的操作都會產生新的集合
2)heterogeneous 多相(異種的),集合中的元素不要求必須是同一種資料類型,可以是不同類型資料的集合
3)persistent 持久化的,集合的內容是不可修改的,相關的操作會產生新的集合,但並不是複製的方式,而是有點類似組態管理的機制,只是新增變更的部分,老的部分還是採用原來的資料。這樣所有的曆史資料都能完整的保留下來。
二、集合的三大操作
Clojure提供了大量的內建通用函數可以操作各種集合。注意沒有函數可以修改集合,因為Clojure的集合是不可修改的,但有很多函數可以產生一個新的集合。
1、filter函數
filter函數是函數式編程中對集合操作的三大重要操作之一,幾乎所有的支援函數式編程的語言都有類似的方法。
其作用是篩選出滿足條件的元素組成一個新的集合返回。
filter函數需要兩個參數,第一個是過濾函數,用於檢查元素是否符合,第二個是集合本身。結果返回一個list。
如下面例子:
例1:
user=> (def stooges ["Moe" "Larry" "Curly" "Shemp"])
#‘user/stooges
user=> (filter #(> (count %) 3) stooges)
("Larry" "Curly" "Shemp")
上面代碼中的count函數是計算字串的長度, #(> (count %) 3) 是個匿名函數,只有長度大於3的字串才滿足條件。
例2:
user=> (def years [1940 1944 1961 1985 1987])
#‘user/years
user=> (filter #(even? %) years)
(1940 1944)
2、map
map函數是函數式編程中對集合操作的三大重要操作之一,幾乎所有的支援函數式編程的語言都有類似的方法。
其作用是 對集合中的每一個元素做處理,最後得到一個新的集合(注意集合類型是列表),新集合的元素個數和原集合一樣,但內容可以不一樣(包括元素的類型)。
所以map函數 的第一個參數是對元素轉換的處理函數,後面的參數是待處理的集合(一個或多個)。
下面我們舉例來說明:
例1:
(defn fun [item] (* item 2)) //定義了一個函數,返回的值是對輸入參數乘以2
(map fun [1 2 3]) //map函數使用了fun函數,最後map函數調用後的返回結果為 (2 4 6)
可以看出,被處理的集合是vector,但處理後返回的集合類型為list
例2:
user=> (map fun #{1 2 3}) (2 6 4)
可以看出,set被處理後返回的集合類型也是列表,而且因為set本身是無序的,返回的list結果序號與set表面上看的也不一致。
例3:
user=> (map + [2 4] [5 6] [1 2]) (8 12)
user=> (map + [2 4 7] [5 6] [1 2 3 4]) (8 12)
上面兩個例子傳入的第一個參數是函數是 + , 後面是多個集合。最後的結果是按照最小的集合元素算的。
例4:
user=> (map #(* % 2) [1 2 3]) (2 4 6)
上面代碼中傳給map的是一個匿名函數 #(* % 2) 。在實際的集合map操作中,大量情境下會傳入匿名函數。
Clojure中的匿名函數就類似於 python、java8中的lambda運算式。
3、reduce
reduce函數是函數式編程中對集合操作的三大重要操作之一,幾乎所有的支援函數式編程的語言都有類似的方法。
其作用是對集合做處理,得到一個計算後的值。 如sum ,count, max, min 都是reduce操作的特例,只不過這些操作是非常常見和通用的 ,會被提為專門的方法。
如:
user=> (reduce #(+ %1 %2) [1 2 3])
6
上面操作是對集合求和。reduce的第一個參數是一個函數,這裡是匿名函數,該匿名第一個參數(用1%代替)是每次迭代的傳回值,%2是元素。
每次對元素操作,1%都會重新最後作為參數傳入,最後一個元素處理完後%1的值會作為reduce的函數值返回。
user=> (reduce #(* %1 %2) [2 4 6])
48
上面操作是對集合中的元素求乘積。
user=> (reduce #(if (> %1 %2) %1 %2) [10 2 54 3 6])
54
user=> (reduce #(if (< %1 %2) %1 %2) [10 2 54 3 6])
2
上面的兩個操作分別是取最大值和最小值。
三、集合的其它操作
下面介紹的集合的操作都是對上述三大操作的一些特例。
1、count函數
該函數用於擷取集合中的元素個數
user=> (count [19 "yellow" true])
3
user=> (count ‘(19 "yellow" true))
3
user=> (count #{19 "yellow" true})
3
user=> (count {:a 1 :b 2})
2
從上面例子可以看出,count函數對這四種集合都是適合的。
2、reverse
該函數是對集合中的資料進行反轉排列,返回一個新的集合。因為map和set本身是無序的資料結構,所以reverse函數也只對vector和list有意義。
user=> (reverse [2 4 7])
(7 4 2)
user=> (reverse ‘(2 4 7))
(7 4 2)
3、map
map函數的作用是 對集合中的每一個元素做處理,最後得到一個新的集合(注意集合類型是列表),新集合的元素個數和原集合一樣,但內容可以不一樣(包括元素的類型)。
所以map函數 的第一個參數是對元素轉換的處理函數,後面的參數是待處理的集合(一個或多個)。
下面我們舉例來說明:
例1:
(defn fun [item] (* item 2)) //定義了一個函數,返回的值是對輸入參數乘以2
(map fun [1 2 3]) //map函數使用了fun函數,最後map函數調用後的返回結果為 (2 4 6)
可以看出,被處理的集合是vector,但處理後返回的集合類型為list
例2:
user=> (map fun #{1 2 3})
(2 6 4)
可以看出,set被處理後返回的集合類型也是列表,而且因為set本身是無序的,返回的list結果序號與set表面上看的也不一致。
例3:
user=> (map + [2 4] [5 6] [1 2])
(8 12)
user=> (map + [2 4 7] [5 6] [1 2 3 4])
(8 12)
上面兩個例子傳入的第一個參數是函數是 + , 後面是多個集合。最後的結果是按照最小的集合元素算的。
例4:
user=> (map #(* % 2) [1 2 3])
(2 4 6)
上面代碼中傳給map的是一個匿名函數 #(* % 2) 。在實際的集合map操作中,大量情境下會傳入匿名函數。
Clojure中的匿名函數就類似於 python、java8中的lambda運算式。
4、apply
該函數的作用是給它傳入一個函數和集合,該函數對整個集合進行操作後返回的結果就是apply函數的返回結果。
user=> (apply + [2 4 6])
12
user=> (apply * [2 4 6])
48
user=> (apply - [2 4 6])
-8
5、從集合中擷取單個元素
user=> (def stooges ["Moe" "Larry" "Curly" "Shemp"])
#‘user/stooges
user=> (first stooges)
"Moe"
user=> (second stooges)
"Larry"
user=> (last stooges)
"Shemp"
user=> (nth stooges 2)
"Curly"
最後一個方法nth的第2個參數表示擷取第幾個元素(從0開始,這裡2代表第3個元素)。
6、從集合中擷取多個元素
user=> (def stooges ["Moe" "Larry" "Curly" "Shemp"])
#‘user/stooges
user=> (next stooges)
("Larry" "Curly" "Shemp")
user=> (nthnext stooges 1)
("Larry" "Curly" "Shemp")
user=> (nthnext stooges 2)
("Curly" "Shemp")
user=> (butlast stooges)
("Moe" "Larry" "Curly")
user=> (drop-last 1 stooges)
("Moe" "Larry" "Curly")
user=> (drop-last 2 stooges)
("Moe" "Larry")
7、some
該函數可以用來判斷集合中是否包含某個元素,需要一個用來判斷的函數作為參數,另一個參數是集合本身。如:
user=> (def stooges ["Moe" "Larry" "Curly" "Shemp"])
#‘user/stooges
user=> (some #(= % "Moe") stooges)
true
user=> (some #(= % "Mark") stooges)
nil
可以看出,如果存在則返回true,否則返回nil(為何不返回false呢?)。
上面的寫法會看起來比較笨拙,可以用如下的方法達到同樣目的:
user=> (contains? (set stooges) "Moe")
true
user=> (contains? (set stooges) "Mark")
false
上面操作,利用set方法將vector轉換為set集合,然後利用contains?函數進行判斷,看上去會更清晰簡單些。
另外一個區別是,當元素不存在時返回的不是nil,而是false
Clojure學習03:資料結構(集合)