序列
我们知道clojure是Lisp的一种方言,那么这也意味着对这门语言必然植根于“列表解析”。但是在Clojure中,我们优先使用"序列"来创造列表和管理列表中的元素。
列表
之前我们说过,Lisp系列语言整个都建立在列表之上。我们使用"list"函数来创建一个列表,但后面你就会发现创建列表的方式不只一种。如果你不想让你列表中的元素被解释执行,记得引用(quote)一下。
=> (list "truck" "car" "bicycle" "plane") ;;创建一个列表
("truck" "car" "bicycle" "plane")
;;与上面方式等价
=> '("truck" "car" "bicycle" "plane")
("truck" "car" "bicycle" "plane")
;;查看列表的类型
=>(class '("truck" "car" "bicycle" "plane"))
clojure.lang.PersistentList
;; 给创建的列表绑定一个全局变量
=> (def vehicles (list "truck" "car" "bicycle" "plane"))
#'user/vehicles
;;查看是否是序列
=>(seq? vehicles)
true
;;查看是否是列表
=>(list? vehicles)
true
;;查看是否是集合
=> (coll? vehicles)
true
;;获取第一个元素
=>(first vehicles)
"truck"
;;获取第二个元素
=>(second vehicles)
"car"
;;获取最后一个元素
=>(last vehicles)
"plane"
;;获取除第一个元素以外的剩下列表
=>(rest vehicles)
("car" "bicycle" "plane")
;;获取第n个元素
=>(nth vehicles 0)
"truck"
=>(nth vehicles 1)
"car"
;;添加元素 (这只是返回一个新的列表,vehicles 并不会被改变)
=> (conj vehicles "motorcycles")
("motorcycles" "truck" "car" "bicycle" "plane")
Cons
Cons是lisp语言中另一个类似列表的一种数据结构。术语”cons“意思就是构造一个对(pair),将这些对链接在一起然后形成一个类似列表的数据结构。就像list一样,cons既是一个类型,也是一个函数,我们可以使用cons来创建这种数据结构。
=>(cons "truck" (list "car" "bicycle" "plane"))
("truck" "car" "bicycle" "plane")
=>(class (cons "truck" (list "car" "bicycle" "plane")))
clojure.lang.Cons
=>(def vehicles (cons "truck" (list "car" "bicycle" "plane")))
#'user/vehicles
=>(seq? vehicles)
true
=>(list? vehicles)
false
=>(coll? vehicles)
true
=>(conj vehicles "motorcycle")
("motorcycle" "truck" "car" "bicycle" "plane")
=>(class (conj vehicles "motorcycle"))
clojure.lang.Cons
=>(cons "motorcycle" vehicles)
("motorcycle" "truck" "car" "bicycle" "plane")
=>(class (cons vehicles "motorcycle"))
clojure.lang.Cons
=>(conj "truck" nil)
java.lang.ClassCastException: cannot be cast to clojure.lang.IPersistentCollection
=>(cons "truck" nil)
("truck")
=>(class (cons "truck" nil))
clojure.lang.PersistentList
注意最后一个例子,是不是看起来很奇怪?当我们使用cons将一个元素附加到一个列表或者另一个cons结构上时,返回的仍然是一个cons类型结构。但是当我们将一个item附加nil上时,返回的却是list类型。这一点尤其注意。
向量
向量是除了list和cons之外的另一个很受欢迎的数据结构,因为它有时用起来有一些独特的优势。举个例子,因为向量使用方括号来表示,所以至少从视觉上来说会让它从大量的圆括号中脱颖而出,提供了更好的可读性。另外使用向量通常能提供比列表更好的性能优势。
=>(vector "truck" "car" "bicycle" "plane")
["truck" "car" "bicycle" "plane"]
;;一种简便的创建向量方式,这个不需要“引用”了哦
=>["truck" "car" "bicycle" "plane"]
["truck" "car" "bicycle" "plane"]
=>(class ["truck" "car" "bicycle" "plane"])
clojure.lang.PersistentVector
=>(def vehicles ["truck" "car" "bicycle" "plane"])
#'user/vehicles
=>(seq? vehicles)
false
=>(list? vehicles)
false
=>(vector? vehicles)
true
=>(coll? vehicles)
true
注意:虽然大多数函数对待向量和列表都会产生相同的结果,但有时候这种假设往往会引入一些问题。看下面例子,注意二者之间的区别:
=> (conj ["a" "b"] "c")
["a" "b" "c"]
=> (conj '("a" "b") "c")
("c" "a" "b")