# 第四章 VimL 数据结构进阶 ## 4.2 通用的字典结构 为什么说字典是通用结构。因为在 VimL 中字典是最复杂的内置类型了,而更复杂的数据 结构都能以字典为基础构建出来。不过从基础的概念上理解,字典与列表其实也有些相似 之处,当掌握了列表之后,对字典的用法也就容易了。 ### 字典与列表的异同 在其他一些(脚本)语言中,对应 VimL 的列表与字典的概念,也叫数组与关联数组。所 以字典也可以看成是一种特殊的列表,无序的以字符串为索引的列表。字典的索引也叫键 ,在 VimL 中,字典的键只能是字符串,当数字用作字典键时也被隐式转为字符串。其它 类型的值,一般不能用作字典的键。 请看这个示例: ```vim : 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)` 返回一个列表,该列表的每个元素又是个小列表,包 含键与值两个元素。所以你明白了,字典的元素,不像列表的元素那么简单的一个值,而 是一个“键值”对。所谓关联数组名称也源于此,每个键对应一个值。键是唯一的,但值可 不唯一,即不同键可关联相同的值。 在字典循环中,也用到了上节介绍的列表解包的多重赋值的功能,相当于如下语句: ```vim : let [k, v] = items(dict)[0] : let [k, v] = items(dict)[1] : ... : let [k, v] = items(dict)[9] ``` 也因此,`[k, v]` 必须用中括号括起来。 在这个特殊的例子中,遍历字典所得的值也许是与列表一样有序。但请记住,字典不保证 有序。同时,在一般应用中,最好不要用连续的数字作为字典的键,那应该直接使用列表 更高效且方便。但如果是很稀疏的有大量空洞的列表,则用字典或许是有意义的。如: ```vim : let dict[10] = 'a' : let dict[100] = 'b' : let dict[1000] = 'c' ``` 这样,只为 dict 增加了三个元素。但若为 `list[1000]='c'` 赋值,则会为列表增加 1000 个元素,中间的无用索引都浪费了。 在常用使用字典时,建议用简单字符串索引,所谓简单字符串,即是可充当 VimL 标记符 (变量名)的字符串。这时,字典的中括号索引可用点索引简化写法,即 `dict['name']` 可简化等效于 `dict.name`。当索引是一个字符串变量时,用中括号索引更方便,即 `dict[varname]`。 还有一点需要重点理解的是,字典变量与列表变量一样,是引用而已。请看以下示例: ```vim : let d1 = {} : let d2 = {} : echo d1 == d2 : echo d1 is d2 : let d3 = d1 : echo d3 is d1 ``` 虽然 `d1` 与 `d2` 都是空字典,它们按值比较是一样的,但其实是不同的字典实体,用 `is` 比较显示不一样。为另一个变量 `d3` 赋值后,就指向相同的字典实体了。 ### 操作字典的内置函数 上节介绍的许多关于列表的函数,也可作用于字典。只是其他参数意义可能不一样,对于 字典时,一般是根据键来处理的。详情请查阅 `:h dict-functions`。 但以下几个函数是字典特有的: * has\_key(dict, key) 检查一个字典是否含有某个键。 * keys(dict) 返回由字典的所有键组成的列表。 * values(dict) 返回由字典的所有值组成的列表。 * items(dict) 返回由字典的所有键值对组成的列表。 这几个返回列表的函数一般用于 `for ... in` 循环中。字典的内部存储是无序的,但可 用 `for ... in sort(keys(dict))` 根据键顺序遍字典。