Clojure是一门动态类型的语言,运行时才会做类型检查。它也不会像java这种面向对象编程语言,在调用一个对象的函数时,首先这个函数必须是属于该对象的函数,否则检查报错。所以对于clojure中的数据类型为Vector或者List的变量,总是会让初学者在选择时比较发愁,不知道什么情况下使用vector好?什么情况下使用list好?哪些函数是接收list作为参数的?而哪些函数又是接收vector作为参数的?它们的区别又是什么?本博客关于近期对clojure的list和vector的学习做一个总结:
1.List与Vector的特点:
Clojure中既有列表(list),也有向量(vector),两者都是序列数据结构,它们的特点对比如下:
- 存储类型:
list是链式存储结构,而vector是顺序存储类型 - 数据插入:
list支持高效的前端插入,而vector在尾端插入会更高效 - 数据查找:
在数据查找方面,因为存储类型的不同,vector的时间复杂度会明显低于list,因为vector采取的理想b树的存储结构(http://blog.csdn.net/zdplife/article/details/52138512),所以其查找效率接近常数时间(O(log32n)),而list的查找操作需要O(N)的时间复杂度,所以在存在大量查找操作时,尽量选择vector。
2.创建相关函数:
- vector的相关创建函数:
;;方法1:直接使用字面值常量表示法
[1 2 3]
;;=> [1 2 3]
;;方法2:使用vector,接收n个元素
(vector 1 2 3)
;;=> [1 2 3]
;;方法3:在已有结构的基础上创建向量
(vec '(1 2 3))
;;=> [1 2 3]
;;map中的元素转换为序列时,是以键值对的形式作为元素返回的
(vec {
:a 1 :b 2})
;;=> [[:a 1] [:b 2]]
;;方法4:使用into函数在已有结构基础上创建向量
(into [] {
:a 1 :b 2})
;;=> [[:a 1] [:b 2]]
(into [] '(1 2 3))
;;=> [1 2 3]
以上是vector四个创建函数,经常用到的是最后两个,我在博客(http://blog.csdn.net/zdplife/article/details/52138512)中讲到,因为into使用了transient(瞬态存储结构),所以其效率会vec函数快30%,vec的可读性比较强,而into函数的效率更大,所以在使用时可以折中选择。
还有就是map被转换为序列时,是按照键值对返回的,这个在写代码时需要注意。
- list的相关创建函数:
list的创建函数与vector的类似,如下:
;;由于()在clojue中会被当作函数操作符,所以这里在用字面值常量创建列表时,需要加单引号,阻止求求值
'(1 2 3)
;;=> (1 2 3)
;;该list与vector的用法类似
(list 1 2 3)
;;=> (1 2 3)
;;该into用法与上述介绍的一致
(into '() [1 2 3])
;;=> (3 2 1)
into在效率上依然是首要的选择,而使用单引号创建list时,有一点缺点就是它不会解释里面的变量或者函数调用,所以这种方法要尽量少用:
(def a 4)
;;=> #'insight.main/a
;;单引号会阻止对a求值,返回字面值
'(1 2 a)
;;=> (1 2 a)
;;而list函数则会对a进行解析,所以尽量选择list函数
(list 1 2 a)
;;=> (1 2 4)
其中在使用into函数时,vector和list中元素的序列是不一样的,这是因为into函数会以此调用conj函数将后者的元素一个一个加入到前面的序列中,但由于这两个存储