这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战
字符串
在任何语言中,字符串都是最基本的类型,比如 java 里就是 String 类型。AviatorScript 中同样支持字符串,只要以单引号或者双引号括起来的连续字符就是一个完整的字符串对象,例如:
"hello world"'hello world'
"a"或者'a'
字符串可以直接通过 println 函数打印
字符串的长度可以通过 string.length 函数获取:
## examples/string.av
let a = "hello world";
println(a);
println(string.length(a));
打印:
hello world
11
字符串拼接可以用 + 号(这又是一个运算符重载):
## examples/string.av
let a = "hello world";
let b = 'AviatorScript';
println(a);
println(string.length(a));
println(a + ',' + b + 5);
字符串拼接 a + ',' + b + 5 包括了数字 5 和字符串 ',' , 任何类型和字符串相加,都将拼接为字符串,这跟 java 的规则一致。因此上面最后一行代码将打印 hello world,AviatorScript5 。
字符串还包括其他函数,如截取字符串 substring,都在 string 这个 namespace 下,具体见函数库列表。
转义
同样,和其他语言类似,遇到特殊字符,AviatorScript 中的字符串也支持转义字符,和 java 语言一样,通过 `` 来转义一个字符,比如我们想表示的字符串中有单引号,如果我们继续使用单引号来表示字符串,这时候就需要用到转义符:
## examples/escape_string.av
println('Dennis's car');
println('AviatorScript is great.\r\nLet's try it!');
``
特殊字符,比如 `\r` 、 `\n` 、 `\t` 等也是同样支持。上述例子我们使用了换行 `\r\n` ,将打印:
Dennis's car AviatorScript is great. Let's try it!
当然,针对引号这个情况,这里你可以简单用双引号来表示字符串,就可以避免转义:
println("Dennis 's car");
### 字符串插值(String Interpolation)
字符串拼接可以用加法,比如
let name = "aviator"; let s = "hello," + name;
拼接后的字符串 s 就是 `hello,aviator` 。 `+` 加法对字符串拼接做了特别优化,内部会自动转化成 `StringBuilder` 来做拼接。但是对于更复杂的场景,字符串拼接的语法仍然显得过于丑陋和繁琐,因此 5.1.0 开始, AviatorScript 支持了字符串插值,一个例子:
examples/string_interpolation.av
let name = "aviator"; let a = 1; let b = 2; let s = "hello, #{name}, #{a} + #{b} = #{a + b}"; p(s);
字符串中 `#{}` 括起来的表达式都将在当前上下文里自动执行求值,然后插入到最终的结果字符串,上面的例子将输出:
hello, aviator, 1 + 2 = 3
AviatorScript 内部做了大量优化,在编译模式复用 Expression 的情况下性能比使用加法拼接字符串更快。
## 布尔类型和逻辑运算
布尔类型用于表示真和假,它只有两个值 `true` 和 `false` 分别表示真值和假值。
比较运算如大于、小于可以产生布尔值:
examples/boolean.av
println("3 > 1 is " + (3 > 1)); println("3 >= 1 is " + (3 >= 1)); println("3 >= 3 is " + (3 >= 3)); println("3 < 1 is " + (3 < 1)); println("3 <= 1 is " + (3 <= 1)); println("3 <= 3 is " + (3 <= 3)); println("3 == 1 is " + (3 == 1)); println("3 != 1 is " + (3 != 1));
输出:
3 > 1 is true 3 >= 1 is true 3 >= 3 is true 3 < 1 is false 3 <= 1 is false 3 <= 3 is true 3 == 1 is false 3 != 1 is true
上面演示了所有的逻辑运算符:
- `>` 大于
- `>=` 大于等于
<!---->
- `<` 小于
- `<=` 小于等于
<!---->
- `==` 等于
- `!=` 不等于
### 逻辑运算和短路规则
布尔值可参于逻辑与、逻辑或、逻辑否等运算,假设 `x` 和 `y` 的返回结果是布尔值:
- `x && y` 表示**并且**的关系,x 为真,并且 y 为真的情况下,结果为 true,否则 false。
- `x || y` 表示**或者**的关系, x 为真,或者 y 为真,结果就为 true,两者都为假值的时候结果为 false。
<!---->
- `!x` **否定**运算符,如果 x 为 true,则结果为 false,反之则为 true。
`&&` 和 `||` 都支持**短路规则**,
- 如果 `x` 为假值, `x && y` 直接返回 false, y 就不进行求值。
- 如果 `x` 为真值, `x || y` 直接返回 true, y 也不进行求值。
examples/boolean.av
let result = false && println('not reachable'); println(result);
let result = true || println('not reachable'); println(result);
`println` 返回结果不是布尔值,如果参与执行逻辑运算会报错,但是由于短路规则, `println` 都不会执行,也就不会打印 `not reachable` ,直接返回 `false` 和 `true` 。
## 三元运算符
布尔值可用于三元表达式和条件语句作为判断,决定执行哪个分支代码。这里先介绍下三元表达式 `test ? stmt1: stmt2` ,当 `test` 的执行结果为 true 的时候,执行 `stmt1` ,反之则执行 `stmt2` ,结果即为 `stmt1` 或者 `stmt2` 的执行结果:
examples/ternary.av
let a = 3; let b = 1;
let c = a > b ? println("a > b") : println("a <= b");
println(c);
将打印:
a > b null ``
可见,只执行了 println("a > b") ,而 false 的分支确实没有执行。返回结果是 println 的返回结果 null 。
跟 java 不同的是 AviatorScript 允许两个分支返回的结果类型可以是不兼容的:
## examples/ternary.av
let a = 3;
let b = 1;
let c = a > b ? "a > b" : 999;
println(c);
a > b ? "a > b" : 999 两个分支的结果分别是字符串和数字,这在 AviatorScript 中是完全可以的,这里将返回字符串 a > b 。
正则表达式
AviatorScript 中正则表达式也是一等公民,作为基本类型来支持, / 括起来的正则表达式就是一个 java.util.Pattern 实例,例如 /\d+/ 表示 1 个或者多个数字,正则表达式语法和 java 完全相同,但是对于需要转义的字符不需要连续的反斜杠 \ ,只要一个 `` 即可,比如我们要匹配 .av 为结尾的文件,正则可以写成 /^.*.av$/ ,这里的 . 来转义后缀里的 . 符号。
正则表达式能参与的运算只有比较运算符和正则匹配运算符 =~ :
## examples/regexp.av
let p = /^(.*).av$/
println(p == p); ## print true
println("regexp.av" =~ p); ##print true
println("$0=" + $0);
println("$1=" + $1);
我们定义了一个正则表达式 p,用于匹配以 .av 结尾的文件名,匹配是用 =~ 运算符,匹配运算符左侧是字符串,右侧是正则表达式,如果匹配成功,返回 true,否则返回 false。
这里 regexp.av 是匹配成功,因此打印 true , 如果匹配成功,同时 AviatorScript 会将正则中的匹配的分组放入 $0 , $1 , $2 ... 的变量中,其中 $0 表示匹配的整个字符串,而 $1 表示第一个分组,以此类推。这里就是文件名,正则中用括号括起来的第一个分组 (.*) 。
因此上面将打印:
true
true
$0=regexp.av
$1=regexp
如果你不想自动捕获分组并放入美元符开头的变量,可以通过设置 Options.PUT_CAPTURING_GROUPS_INTO_ENV 为 false 来关闭此行为。