4.2 通用的字典结构

优质
小牛编辑
135浏览
2023-12-01

4.2 通用的字典结构

为什么说字典是通用结构。因为在 VimL 中字典是最复杂的内置类型了,而更复杂的数据 结构都能以字典为基础构建出来。不过从基础的概念上理解,字典与列表其实也有些相似 之处,当掌握了列表之后,对字典的用法也就容易了。

字典与列表的异同

在其他一些(脚本)语言中,对应 VimL 的列表与字典的概念,也叫数组与关联数组。所 以字典也可以看成是一种特殊的列表,无序的以字符串为索引的列表。字典的索引也叫键 ,在 VimL 中,字典的键只能是字符串,当数字用作字典键时也被隐式转为字符串。其实 类型的值,一般不能用作字典的键。

请看这个示例:

: let list = range(10)
: let dict = {}
: for i in range(10)
:     let dict[i] = i
: endfor

: echo 'list =' list
: echo 'dict =' dict

: for [k, v] in items(dict)
:    echo k v
: endfor

用 range() 创建了一个列表 list,包含的元素是 0-9 这十个数字。然后创建了一个空 字典 dict,再用循环为字典增加键值,也用相同的 0-9 这十个数字作为键与值。这样, 在表观上,dict 与 list 似乎保存着相同的元素,用相同的索引能得到相同的值,比如 dict[5]list[5] 都得到数值 5。但通过 :echo dict 可以发现,dict 字典 的键,其实不是数字,而是字符串('0', '1'等)。

为理解遍历字典的范式 for [k, v] in items(dict),可先用 echo items(dict) 查 看这是什么。可见 items(dict) 返回一个列表,该列表的每个元素又是个小列表,包 含键与值两个元素。所以你明白了,字典的元素,不像列表的元素那么简单的一个值,而 是一个“键值”对。所谓关联数组名称也源于此,每个键对应一个值。键是唯一的,但值可 不唯一,即不同键可关联相同的值。

在字典循环中,也用到了上节介绍的列表解包的多重赋值的功能,相当于如下语句:

: let [k, v] = items(dict)[0]
: let [k, v] = items(dict)[1]
: ...
: let [k, v] = items(dict)[9]

也因此,[k, v] 必须用中括号括起来。

在这个特殊的例子中,遍历字典所得的值也许是与列表一样有序。但请记住,字典不保证 有序。同时,在一般应用中,最好不要用连续的数字作为字典的键,那应该直接使用列表 更高效且方便。但如果是很稀疏的有大量空洞的列表,则用字典或许是有意义的。如:

: let dict[10] = 'a'
: let dict[100] = 'b'
: let dict[1000] = 'c'

这样,只为 dict 增加了三个元素。但若为 list[1000]='c' 赋值,则会为列表增加 1000 个元素,中间的无用索引都浪费了。

在常用使用字典时,建议用简单字符串索引,所谓简单字符串,即是可充当 VimL 标记符 (变量名)的字符串。这时,字典的中括号索引可用点索引简化写法,即 dict['name'] 可简化等效于 dict.name。当索引是一个字符串变量时,用中括号索引更方便,即 dict[varname]

还有一点需要重点理解的是,字典变量与列表变量一样,是引用而已。请看以下示例:

: let d1 = {}
: let d2 = {}
: echo d1 == d2
: echo d1 is d2
: let d3 = d1
: echo d3 is d1

虽然 d1d2 都是空字典,它们按值比较是一样的,但其实是不同的字典实体,用 is 比较显示不一样。为另一个变量 d3 赋值后,就指向相同的字典实体了。

操作字典的内置函数

上节介绍的许多关于列表的函数,也可作用于字典。只是其他参数意义可能不一样,对于 字典时,一般是根据键来处理的。详情请查阅 :h dict-functions

但以下几个函数是字典特有的:

  • has_key(dict, key) 检查一个字典是否含有某个键。
  • keys(dict) 返回由字典的所有键组成的列表。
  • values(dict) 返回由字典的所有值组成的列表。
  • items(dict) 返回由字典的所有键值对组成的列表。

这几个返回列表的函数一般用于 for ... in 循环中。字典的内部存储是无序的,但可 用 for ... in sort(keys(dict)) 根据键顺序遍字典。