在刚刚接触clojure这门语言时,总是容易搞不清楚某个函数都适用于哪些的数据结构,为什么有的函数可以用于vector,却不能用于list呢。其实主要是因为各种类型实现的接口不同,本文总结一下Clojure中的实现关联数据接口的数据结构(Vector/Map/Set),以及这些数据结构拥有哪些公共的函数可以操作。
1.clojure中的关联数据结构
clojure中有三种常用的关联的数据结构:PersistentVector、PersistentHashSet、Map,其中Map又分为三种类型:PersistentArrayMap、PersistentTreeMap、PersistentHashMap。这三种数据结构都显式或者隐式存在着某种类似key-value的映射关系。
- 映射表(Map):
map的映射关系是显式存在的,每个map都由多个key-value对组成:
;;定义一个map
(def m {:a 1 :b 2})
;;=> #'user/m
m
;;=> {:a 1, :b 2}
- 向量(PersistentVector):
clojure中向量类似其它语言中数组,定义如下:
;;定义一个向量
(def v [1 7 8 2])
;;=> #'user/v
v
;;=> [1 7 8 2]
因为向量中的元素是顺序存储的,所以存在默认的映射关系:从元素所在的序号(key)到该元素(value)的映射,也就是0->1 | 1->7 | 2->8 | 3->2,所以大家就可以很容易的理解以下操作的含义:
;;获取序号为1的元素
(get [2 7 9 6 3] 1)
;;=> 7
;;在list中使用get函数则会返回nil(不是我们想要的结果)
(get '(2 7 9 6 3) 1)
;;=> nil
;;assoc函数绑定对应序号的值,更改的范围是[0 (count [2 7 9 6 3])]
(assoc [2 7 9 6 3] 2 "hello")
;;=> [2 7 "hello" 6 3]
;;update用一个函数更改对应序号的值,更改的范围是[0 (count [2 7 9 6 3])]
(update [1 2 3] 0 str)
;;=> ["1" 2 3]
- 集(Set)
clojure的set数据结构中存放的是不重复的相互之间可比较的元素,数据结构如下:
(set [1 7 6 1])
;;=> #{7 1 6}
该数据结构也存在着默认的映射关系:从自身元素到自身元素的映射,因为其没有重复的元素,可以保证key的唯一性。知道这个,然后你就会对以下的操作认为理所当然了:
;;用get函数判断某个元素是否存在
(if (nil? (get #{1 7 8} 7))
"not exist"
"exist")
;;=> "exist"
;;如果元素是关键字,就可以使用关键字作为函数获取其中的元素
(:year #{:city :year :age})
;;=> :year
2.与关联数据结构相关的操作函数
这三种数据结构经常用到的相对应的操作函数总结如下:
- 向量(Vector):
因为向量的元素是顺序存储的,因此和元素位置有关系的函数都可以认为是与关联数据结构相关的函数:
(1)get/get-in函数——获取某个index上对应的元素;
(2)assoc函数/assoc-in函数——更改某个index上的元素;
(3)update函数/update-in函数——-用函数更改某个index上的元素;
(4)contains?函数——–判断一个vector中是否存在某个index,该函数用处不大;
(5)vector本身作为函数——–vector本身作为函数,获取某个序号的元素; - 集(Set):
Set涉及到关联数据结构的相关函数如下:
(1)关键字作为函数——判断该关键字是否存在;
(2)get函数/get-in函数——判断该关键字是否存在; - 映射表(Map):
Map本来就是纯粹的映射关系,所以其涉及到函数基本都属于映射关系,其中以上在Vector和Set中讲到的函数都可以在Map中使用,对于每个数据结构的具体操作函数,我会在其它博客里详细列出,这里就不一一列举了。 - 关联数据结构之间嵌套操作:
这三个关联数据结构所涉及到的共有函数,比如get-in,assoc-in可以在包含多种不同的嵌套数据结构中使用,非常方便:
;;使用get-in函数获取嵌套关联数据结构中的值
(get-in {:a ["blue" "red"] :b ["yellow" "black"]} [:a 1])
;;=> "red"
(get-in [{:a 1 :b 2} {:a 7 :b 9}] [1 :b])
;;=> 9
(get-in [#{1 7 9} #{2 6 3}] [1 6])
;;=> 6
;;使用assoc-in更改嵌套关联数据结构中的值
(assoc-in {:a ["blue" "red"] :b ["yellow" "black"]} [:a 1] "white")
;;=> {:a ["blue" "white"], :b ["yellow" "black"]}
;;使用update-in和函数str更改关联嵌套数据结构中的值
(update-in {:a ["blue" "red"] :b ["yellow" "black"]} [:a 1] str "not-")
;;=> {:a ["blue" "rednot-"], :b ["yellow" "black"]}
3.总结
clojure中的不同数据结构之间的操作函数比较容易混淆,到底哪些函数该用在哪些数据结构中,就比如List和Vector,有些是公共使用函数,有些却只能用在Vector上。该文章主要总结了clojure中关联数据结构以及其中经常使用的一些函数,可以更好的区分一些函数的使用范围。