集合

Clojure提供这些集合类型: list, vector, set, map。同时Clojure还可以使用Java里面提供的将所有的集合类型,但是通常不会这样做的, 因为Clojure自带的集合类型更适合函数式编程。

Clojure集合有着java集合所不具备的一些特性。所有的clojure集合是不可修改的、异源的以及持久的。不可修改的意味着一旦一个集合产生之后,你不能从集合里面删除一个元素,也往集合里面添加一个元素。异源的意味着一个集合里面可以装进任何东西(而不必须要这些东西的类型一样)。持久的以为着当一个集合新的版本产生之后,旧的版本还是在的。CLojure以一种非常高效的,共享内存的方式来实现这个的。比如有一个map里面有一千个name-valuea pair, 现在要往map里面加一个,那么对于那些没有变化的元素, 新的map会共享旧的map的内存,而只需要添加一个新的元素所占用的内存。

有很多核心的函数可以用来操作所有这些类型的集合。。多得以至于无法在这里全部描述。其中的一小部分我们会在下面介绍vector的时候介绍一下。要记住的是,因为clojure里面的集合是不可修改的,所以也就没有对集合进行修改的函数。相反clojure里面提供了一些函数来从一个已有的集合来高效地创建新的集合 — 使用 persistent data structures 。同时也有一些函数操作一个已有的集合(比如vector)来产生另外一种类型的集合(比如LazySeq), 这些函数有不同的特性。

提醒: 这一节里面介绍的Clojure集合对于学习clojure来说是非常的重要。但是这里介绍一个函数接着一个函数,所以你如果觉得有点烦,有点乏味,你可以跳过,等用到的时候再回过头来查询。

count 返回集合里面的元素个数,比如:

(count [19 "yellow" true]) ; -> 3

conj 函数是 conjoin的缩写, 添加一个元素到集合里面去,到底添加到什么位置那就取决于具体的集合了,我们会在下面介绍具体集合的时候再讲。

reverse 把集合里面的元素反转。

(reverse [2 4 7]) ; -> (7 4 2)

map 对一个给定的集合里面的每一个元素调用一个指定的方法,然后这些方法的所有返回值构成一个新的集合(LazySeq)返回。这个指定了函数也可以有多个参数,那么你就需要给map多个集合了。如果这些给的集合的个数不一样,那么执行这个函数的次数取决于个数最少的集合的长度。比如:

; The next line uses an anonymous function that adds 3 to its argument.
(map #(+ % 3) [2 4 7]) ; -> (5 7 10)
(map + [2 4 7] [5 6] [1 2 3 4]) ; adds corresponding items -> (8 12)

apply 把给定的集合里面的所有元素一次性地给指定的函数作为参数调用,然后返回这个函数的返回值。所以apply与map的区别就是map返回的还是一个集合,而apply返回的是一个元素, 可以把apply看作是SQL里面的聚合函数。比如:

(apply + [2 4 7]); -> 13

有很多函数从一个集合里面获取一个元素,比如:

(def stooges ["Moe" "Larry" "Curly" "Shemp"])
(first stooges) ; -> "Moe"
(second stooges) ; -> "Larry"
(last stooges) ; -> "Shemp"
(nth stooges 2) ; indexes start at 0 -> "Curly"

也有一些函数从一个集合里面获取多个元素,比如:

(next stooges) ; -> ("Larry" "Curly" "Shemp")
(butlast stooges) ; -> ("Moe" "Larry" "Curly")
(drop-last 2 stooges) ; -> ("Moe" "Larry")
; Get names containing more than three characters.
(filter #(> (count %) 3) stooges) ; -> ("Larry" "Curly" "Shemp")
(nthnext stooges 2) ; -> ("Curly" "Shemp")

有一些谓词函数测试集合里面每一个元素然后返回一个布尔值,这些函数都是”short-circuit”的,一旦它们的返回值能确定它们就不再继续测试剩下的元素了,有点像java的&&和or, 比如:

(every? #(instance? String %) stooges) ; -> true
(not-every? #(instance? String %) stooges) ; -> false
(some #(instance? Number %) stooges) ; -> nil
(not-any? #(instance? Number %) stooges) ; -> true