第二章 VimL 语言基本语法
2.1 变量与类型
VimL 语言的变量规则与其他大多数语言一样,可以(只允许)由字母、数字与下划线组 成,且不能以数字开头。特殊之处在于还可以在变量名之前添加可选的作用域前缀,如 g: l: s: b: w: t:
(a:
又有点特殊,在定义函数参数时不要前缀,而在使用参数时 需要前缀),这在第一章有专门讨论,此不再叙说。
VimL 所支持的变量(值)类型可由帮助(:help type()
)查看。其中最主要最常用的 有数字(number)、字符串(string)、列表(list)与字典(dictionary)四种,或者 可以再进一步归纳为三种,因为前两种(数字与字符串)在绝大数情况下自动转换,在使 用时几乎不必考虑其类型差别,只须知道它表示“一个值”,所以也称作标量。而列表与字 典变量则是“多个值”的集合,所不同在于获取其中某个值的索引方式不同。
标量:数字与字符串
数字是(number)直译,其实就是其他大多数语言所称的整数(int)。数字是有符号的 ,包括正负数与 0 ,其取值范围与 Vim 的编译版本有关。经笔者的测试,vim8.0 支持 8 字节的有符号整数,低版本只支持 4 字节的有符号整数。数字经常(或常规功能)是 用于命令的地址参数表示行号,或命令的重复次数,一般情况下不必考虑数字溢界的问题 。当你需要用到 VimL 来表达很大的整数时,才要小心这个潜在的问题。
字符串也简单,用单引号或双引号括起来即可,它们的语义是完全一致的。不过有以下使 用建议原则:
- 一般使用单引号表示字符串,如
'string'
,毕竟双引号还可用于行末注释,尽量避 免混淆。 - 如果需要使用转义如
\n
\t
,则使用双引号,单引号不支持转义。 - 如果字符串包含一种引号,则使用另一种引号括起整个字符串。
- 如果有包含一层引用,则内外层用不同的引号。
数字变量支持常见的数学运算(加减乘除模,+-*/%
),字符串只支持连接运算符,用 点号(.
)表示。此外,一些内建函数与命令也会要求其参数是数字或字符串。也就是 数字与字符串有不同的使用环境,VimL 便能依据上下文环境将数字或字符串进行自动转 换,规则如下:
- 数字转字符串表示是显而易见的,就是十进制数的10个数字字符表示法;
- 字符串转数字时,只截前面像数字的子串,若不以数字字符开头,则转为数字 0 。
请测试:
: echo 'sring' . 123
: echo '123'
: echo '123' + 1
: echo '123string' + 1
: echo '1.23string' + 1
: echo 'string123' + 1
需要特别注意的是字符串 '1.23'
只会自动转为字数 1
,而不是浮点数 1.23
。
在 VimL 中输入数字常量时,也支持按二进制、八进制、十六进制的表示法,不过自动转 字符串时只按十进制转换。请自行观察以下结果,如果不懂其他进制可无视。
: echo 0xff
: echo 020
: echo 0b10
: echo 0b10 + 3
: echo 'string' . 0xff
列表:有序集合
列表,在其他语言中也有的叫数组,就是许多值的有序集合。VimL 的列表有以下要点:
- 创建列表语法, 中括号整体,逗号分隔元素:
:let list = [0, 1, 2, 3]
- 用中括号加数字索引访问元素:
:echo list[1]
- 索引从 0 开始,支持负索引,-1 表示最后一个元素,访问不存在索引时报错。
- 索引也可以用整数变量。
- 不限长度,在需要时会自动扩展容量。
在列表创建后,用内建函数 add()
与 remove()
动态增删元素,用 len()
函数取 得列表长度(元素个数)。例如:
: let list = [0, 1, 2, 3]
: echo list
: call add(list, 4)
: call add(list, 5)
: echo list
: call remove(list, -1) | echo list
: call remove(list, 1) | echo list
字典:无序集合
字典,在其他语言中可能叫 Hash 表或散列表,就是许多“键-值”对的集合。与列表最大 的不同在于,它不是用数字索引来访问其内的元素,而是用字符串索引(键)来访问元素 。字典在内部存储方式是无序的,但通过键访问元素的效率极快。
定义与使用字典的语法示例如下:
: let dict = {'x': 1, 'y': 2, 'z': 3,}
: echo dict
: echo dict['x']
: echo dict.y
: let var = 'z'
: echo dict[var]
: let dict['u'] = 4
: let dict.v = 5
: echo dict
语法要点:
- 字典用大括号
{}
括号整体。 - 每个键值对用逗号分分隔,键与值用冒号分隔,键一般有引号表字符串。
- 大括号内的空白是可选的,最后一个逗号也是可选的。
- 访问字典内某个元素时,仍是中括号
[]
索引,键放在中括号中。 - 在创建字典或访问元素时,键既可用引号引起的常量字符串,也可用字符串变量,数字 变量自动转换为字符串。
- 当一个键是普通常量字符串(可用作变量名的字符串)时,可不用中括号加引号索引, 而简洁地用点号索引,二者等价。
- 不能访问不存在的键,否则报错。
- 能直接对不存在的键赋值,表示对字典增加一个键值对元素。
删除变量
创建(或叫定义)变量用 :let
命令,相应的也就有 :unlet
命令用于删除一个变量 。一般情况下没必要删除一个标量,因为它也占不了多少内存,需要重定义时也可以重新 赋值。但对于列表与字典,有时比较在意其集合意义,可以用 :unlet
删除其中一个值 ,加上对应的索引即可,如果不加索引,则表示删除整个列表或字典。
例如:假设 list
与 'dict' 变量已如上定义:
: unlet list[1] | echo list
: unlet list[-1] | echo list
: unlet dict['u'] | echo dict
: unlet dict.v | echo dict
如果要删除的变量或字典(列表)不存在的索引,:unlet
会报错。如果想绕过该错误 检测,则可用 :unlet!
命令。
在 vim8.0 版本之前,标量、列表、字典三者是不互通的。如果 list
已被定义成了一 个列表变量,那么它就不能用 :let
重赋值为一个字典或字符串或其他什么,但允许重 赋值为另一个列表变量。如果一定要改变 list
的变量类型,只能先 :unlet
它,再 重新 :let
它为其他任意变量。
在 vim8.0 版本之后,不再有这个限制,不会再报诸如“类型不匹配”的错误了,更好地体 现了动态弱类型的特点。然而,良好的命名规范要求变量名望文生义,在同一个范围的同 一个变量名,前后用之于表达完全不同类型的变量,并不是个好习惯。
浮点数
虽然浮点数在 VimL 中用的比较少,但毕竟还是支持的。
- 浮点数也叫小数,支持科学记数法。
- 数字(整数)可自动转为浮点数。
- 浮点不能自动转为整数,也不能自动转为字符串。
- 整数运算结果仍是整数,浮点数运算结果仍是浮点数。
- 浮点数取整后仍是浮点数,不是整数。
请看以下示例:
: echo 1.23e3
: let int = 123 | let float = 1.23 | let str = 'string'
: echo str . int
: echo str . float |"错误
: echo str . 5 / 3
: echo str . 5 % 3
: echo str . 5 / 3.0 |"错误
: echo str . 5 % 3.0 |"错误
: echo round(5/3.0)
: echo round(5/3.0) == 2
: echo round(5/3.0) . str |"错误
最后一行语句说明,虽然一个浮点数取整后看似与一个整数相等,但它仍然不是整数,所 以不能与字符串自动连接。
要将一个字符串“显式”转换为整数,可以与 0
相加;同理,要将整数“显式”转换为字 符串,可与空串 ""
相连接。VimL 还提供了另一个内建函数 string()
将任意其他 类型转换为可打印字符串。于是,想将一个浮点数圆整为“真正”的整数,可用如下操作:
: echo 0 + string(round(5/3.0))
: echo type(round(5/3.0))
: echo type(0 + string(round(5/3.0)))
注:行末注释可用一个双引号 "
开始,但建议用 |"
更有适用性。|
表示分隔语 句,只是后面一个语句是只有注释的空语句。
类型判断
从 Vim8.0 开始,有一系列 vim 变量专门地用来表示各种变量(值)类型。比如 v:t_list
表示列表类型。如果要判断一个变量是否列表类型,可用以下三种写法中任 何一种(但之前的低版本 Vim 只能用后两种):
if type(var) == v:t_list
if type(var) == 3
if type(var) == type([])
关于具有选择分支功能的 :if
语句,在下一节继续讲解。