Google Aviator语法手册详解
转载自:Google Aviator语法手册 – 知乎 (zhihu.com)
背景
Google Aviator是一种基于字节码的高性能Java表达式求值引擎,专注于提供快速的表达式计算能力。它主要用于动态计算表达式,特别是在需要频繁计算的场景中。Aviator支持的表达式范围包括但不限于以下几个方面:
- 算术和数值运算: Aviator 支持基本的算术运算符(+、-、*、/、%)以及数值运算,包括整数和浮点数。
- 比较和逻辑运算: Aviator 支持比较运算符(==、!=、>、<、>=、<=)和逻辑运算符(&&、||、!),用于比较和操作布尔值。
- 字符串操作: Aviator 允许对字符串进行拼接(+)和比较运算,还支持字符串长度计算。
- 三元表达式: 支持类似于 Java三元表达式(? :),用于条件判断和值选择。
- 注意,跟 java 不同的是 AviatorScript 允许两个分支返回的结果类型可以是不兼容的。比如:a > b ? "a > b" : 999 两个分支的结果分别是字符串和数字,这在 AviatorScript 中是完全可以的。
- 成员访问: Aviator 允许访问对象的成员变量和方法,可以通过`.`符号进行访问。
- 函数调用: Aviator 内置了一些常用的函数,如数学函数、字符串函数等,同时也支持自定义函数的调用。
- 集合操作: Aviator 支持对数组和列表的遍历和访问,可以执行类似于 foreach 的操作。
- 正则表达式: Aviator 提供了对正则表达式的支持,可以用于字符串匹配和替换。
- 条件判断: Aviator 支持 if-else 条件判断,根据条件来执行不同的操作。
- 循环控制: Aviator 支持 for 循环,可以用于多次迭代操作。
- 空值处理: Aviator 支持对空值的判断和处理,可以进行空值的条件判断和操作。
- 位运算: Aviator 支持位运算符(&、|、^、~、<<、>>、>>>),用于对整数进行位操作。
总体而言,Aviator提供了丰富的表达式求值能力,涵盖了算术、逻辑、字符串、集合等多个方面,使得开发人员能够在各种场景下高效地计算和处理表达式。下面我们将根据各个不同的方面展开细节讲解,希望能够帮你提高开发效率。
Aviator 自带方法与使用
类型 | 函数名称 | 说明 |
---|---|---|
系统函数 | assert(predicate, [msg]) | 断言函数,当 predicate 的结果为 false 的时候抛出 AssertFailed 异常, msg 错误信息可选。 |
系统函数 | sysdate() | 返回当前日期对象 java.util.Date |
系统函数 | rand() | 返回一个介于 [0, 1) 的随机数,结果为 double 类型 |
系统函数 | rand(n) | 返回一个介于 [0, n) 的随机数,结果为 long 类型 |
系统函数 | cmp(x, y) | 比较 x 和 y 大小,返回整数,0 表示相等, 1 表达式 x > y,负数则 x < y。 |
系统函数 | print([out],obj) | 打印对象,如果指定 out 输出流,向 out 打印, 默认输出到标准输出 |
系统函数 | println([out],obj)或者p([out], obj) | 与 print 类似,但是在输出后换行 |
系统函数 | pst([out], e); | 等价于 e.printStackTrace(),打印异常堆栈,out 是可选的输出流,默认是标准错误输出 |
系统函数 | now() | 返回 System.currentTimeMillis() 调用值 |
系统函数 | long(v) | 将值转为 long 类型 |
系统函数 | double(v) | 将值转为 double 类型 |
系统函数 | boolean(v) | 将值的类型转为 boolean,除了 nil 和 false,其他都值都将转为布尔值 true。 |
系统函数 | str(v) | 将值转为 string 类型,如果是 nil(或者 java null),会转成字符串 'null' |
系统函数 | bigint(x) | 将值转为 bigint 类型 |
系统函数 | decimal(x) | 将值转为 decimal 类型 |
系统函数 | identity(v) | 返回参数 v 自身,用于跟 seq 库的高阶函数配合使用。 |
系统函数 | type(x) | 返回参数 x 的类型,结果为字符串,如 string, long, double, bigint, decimal, function 等。Java 类则返回完整类名。 |
系统函数 | is_a(x, class) | 当 x 是类 class 的一个实例的时候,返回 true,例如 is_a("a", String) ,class 是类名。 |
系统函数 | is_def(x) | 返回变量 x 是否已定义(包括定义为 nil),结果为布尔值 |
系统函数 | undef(x) | “遗忘”变量 x,如果变量 x 已经定义,将取消定义。 |
系统函数 | range(start, end, [step]) | 创建一个范围,start 到 end 之间的整数范围,不包括 end, step 指定递增或者递减步幅。 |
系统函数 | tuple(x1, x2, …) | 创建一个 Object 数组,元素即为传入的参数列表。 |
系统函数 | eval(script, [bindings], [cached]) | 对一段脚本文本 script 进行求值,等价于 AviatorEvaluator.execute(script, env, cached) |
系统函数 | comparator(pred) | 将一个谓词(返回布尔值)转化为 java.util.Comparator 对象,通常用于 sort 函数。 |
系统函数 | max(x1, x2, x3, …) | 取所有参数中的最大值,比较规则遵循逻辑运算符规则。 |
系统函数 | min(x1, x2, x3, …) | 取所有参数中的最小值,比较规则遵循逻辑运算符规则。 |
系统函数 | constantly(x) | 用于生成一个函数,它对任意(个数)参数的调用结果 x。 |
字符串函数 | date_to_string(date,format) | 将 Date 对象转化化特定格式的字符串,2.1.1 新增 |
字符串函数 | string_to_date(source,format) | 将特定格式的字符串转化为 Date 对 象,2.1.1 新增 |
字符串函数 | string.contains(s1,s2) | 判断 s1 是否包含 s2,返回 Boolean |
字符串函数 | string.length(s) | 求字符串长度,返回 Long |
字符串函数 | string.startsWith(s1,s2) | s1 是否以 s2 开始,返回 Boolean |
字符串函数 | string.endsWith(s1,s2) | s1 是否以 s2 结尾,返回 Boolean |
字符串函数 | string.substring(s,begin[,end]) | 截取字符串 s,从 begin 到 end,如果忽略 end 的话,将从 begin 到结尾,与 java.util.String.substring 一样。 |
字符串函数 | string.indexOf(s1,s2) | java 中的 s1.indexOf(s2),求 s2 在 s1 中 的起始索引位置,如果不存在为-1 |
字符串函数 | string.split(target,regex,[limit]) | Java 里的 String.split 方法一致,2.1.1 新增函数 |
字符串函数 | string.join(seq,seperator) | 将集合 seq 里的元素以 seperator 为间隔 连接起来形成字符串,2.1.1 新增函数 |
字符串函数 | string.replace_first(s,regex,replacement) | Java 里的 String.replaceFirst 方法, 2.1.1 新增 |
字符串函数 | string.replace_all(s,regex,replacement) | Java 里的 String.replaceAll 方法 , 2.1.1 新增 |
数学函数 | math.abs(d) | 求 d 的绝对值 |
数学函数 | math.round(d) | 四舍五入 |
数学函数 | math.floor(d) | 向下取整 |
数学函数 | math.ceil(d) | 向上取整 |
数学函数 | math.sqrt(d) | 求 d 的平方根 |
数学函数 | math.pow(d1,d2) | 求 d1 的 d2 次方 |
数学函数 | math.log(d) | 求 d 的自然对数 |
数学函数 | math.log10(d) | 求 d 以 10 为底的对数 |
数学函数 | math.sin(d) | 正弦函数 |
数学函数 | math.cos(d) | 余弦函数 |
数学函数 | math.tan(d) | 正切函数 |
数学函数 | math.atan(d) | 反正切函数 |
数学函数 | math.acos(d) | 反余弦函数 |
数学函数 | math.asin(d) | 反正弦函数 |
Sequence 函数(集合处理) | repeat(n, x) | 返回一个 List,将元素 x 重复 n 次组合而成。 |
Sequence 函数(集合处理) | repeatedly(n, f) | 返回一个 List,将函数 f 重复调用 n 次的结果组合而成。 |
Sequence 函数(集合处理) | seq.array(clazz, e1, e2,e3, …) | 创建一个指定 clazz 类型的数组,并添加参数 e1,e2,e3 …到这个数组并返回。 clazz 可以是类似 java.lang.String 的类型,也可以是原生类型,如 int/long/float 等 |
Sequence 函数(集合处理) | seq.array_of(clazz, size1, size2, …sizes) | 创建 clazz 类型的一维或多维数组,维度大小为 sizes 指定。clazz 同 seq.array 定义。 |
Sequence 函数(集合处理) | seq.list(p1, p2, p3, …) | 创建一个 java.util.ArrayList 实例,添加参数到这个集合并返回。 |
Sequence 函数(集合处理) | seq.set(p1, p2, p3, …) | 创建一个 java.util.HashSet 实例,添加参数到这个集合并返回。 |
Sequence 函数(集合处理) | seq.map(k1, v1, k2, v2, …) | 创建一个 java.util.HashMap 实例,参数要求偶数个,类似 k1,v1 这样成对作为 key-value 存入 map,返回集合。 |
Sequence 函数(集合处理) | seq.entry(key, value) | 创建 Map.Entry 对象,用于 map, filter 等函数 |
Sequence 函数(集合处理) | seq.keys(m) | 返回 map 的 key 集合 |
Sequence 函数(集合处理) | seq.vals(m) | 返回 map 的 value 集合 |
Sequence 函数(集合处理) | into(to_seq, from_seq) | 用于 sequence 转换,将 from sequence 的元素使用 seq.add 函数逐一添加到了 to sequence 并返回最终的 to_seq |
Sequence 函数(集合处理) | seq.contains_key(map, key) | 当 map 中存在 key 的时候(可能为 null),返回 true。对于数组和链表,key 可以是 index,当 index 在有效范围[0..len-1],返回 true,否则返回 false |
Sequence 函数(集合处理) | seq.add(coll, element)seq.add(m, key, value) | 往集合 coll 添加元素,集合可以是 java.util.Collection,也可以是 java.util.Map(三参数版本) |
Sequence 函数(集合处理) | seq.put(coll, key, value) | 类似 List.set(i, v)。用于设置 seq 在 key 位置的值为 value,seq 可以是 map ,数组或者 List。 map 就是键值对, 数组或者 List 的时候, key 为索引位置整数,value 即为想要放入该索引位置的值。 |
Sequence 函数(集合处理) | seq.remove(coll, element) | 从集合或者 hash map 中删除元素或者 key |
Sequence 函数(集合处理) | seq.get(coll, element) | 从 list、数组或者 hash-map 获取对应的元素值,对于 list 和数组, element 为元素的索引位置(从 0 开始),对于 hash map 来说, element 为 key。 |
Sequence 函数(集合处理) | map(seq,fun) | 将函数 fun 作用到集合 seq 每个元素上, 返回新元素组成的集合 |
Sequence 函数(集合处理) | filter(seq,predicate) | 将谓词 predicate 作用在集合的每个元素 上,返回谓词为 true 的元素组成的集合 |
Sequence 函数(集合处理) | count(seq) | 返回集合大小,seq 可以是数组,字符串,range ,List 等等 |
Sequence 函数(集合处理) | is_empty(seq) | 等价于 count(seq) == 0,当集合为空或者 nil,返回 true |
Sequence 函数(集合处理) | distinct(seq) | 返回 seq 去重后的结果集合。 |
Sequence 函数(集合处理) | is_distinct(seq) | 当 seq 没有重复元素的时候,返回 true,否则返回 false |
Sequence 函数(集合处理) | concat(seq1, seq2) | 将 seq1 和 seq2 “连接”,返回连接后的结果,复杂度 O(m+n), m 和 n 分别是两个集合的长度。 |
Sequence 函数(集合处理) | include(seq,element) | 判断 element 是否在集合 seq 中,返回 boolean 值,对于 java.uitl.Set 是 O(1) 时间复杂度,其他为 O(n) |
Sequence 函数(集合处理) | sort(seq, [comparator]) | 排序集合,仅对数组和 List 有效,返回排序后的新集合,comparator 是一个 java.util.Comparator 实例,可选排序方式。 |
Sequence 函数(集合处理) | reverse(seq) | 将集合元素逆序,返回新的集合。 |
Sequence 函数(集合处理) | reduce(seq,fun,init) | fun 接收两个参数,第一个是集合元素, 第二个是累积的函数,本函数用于将 fun 作用在结果值(初始值为 init 指定)和集合的每个元素上面,返回新的结果值;函数返回最终的结果值 |
Sequence 函数(集合处理) | take_while(seq, pred) | 遍历集合 seq,对每个元素调用 pred(x),返回 true则加入结果集合,最终返回收集的结果集合。也就是说从集合 seq 收集 pred 调用为 true 的元素。 |
Sequence 函数(集合处理) | drop_while(seq, pred) | 与 take_while 相反,丢弃任何 pred(x) 为 true 的元素并返回最终的结果集合。 |
Sequence 函数(集合处理) | group_by(seq, keyfn) | 对集合 seq 的元素按照 keyfn(x) 的调用结果做分类,返回最终映射 map。具体使用见文档。 |
Sequence 函数(集合处理) | zipmap(keys, values) | 返回一个 HashMap,其中按照 keys 和 values 两个集合的顺序映射键值对。具体使用见文档。 |
Sequence 函数(集合处理) | seq.every(seq, fun) | fun 接收集合的每个元素作为唯一参数,返回 true 或 false。当集合里的每个元素调用 fun 后都返回 true 的时候,整个调用结果为 true,否则为 false。 |
Sequence 函数(集合处理) | seq.not_any(seq, fun) | fun 接收集合的每个元素作为唯一参数,返回 true 或 false。当集合里的每个元素调用 fun 后都返回 false 的时候,整个调用结果为 true,否则为 false。 |
Sequence 函数(集合处理) | seq.some(seq, fun) | fun 接收集合的每个元素作为唯一参数,返回 true 或 false。当集合里的只要有一个元素调用 fun 后返回 true 的时候,整个调用结果立即为该元素,否则为 nil。 |
Sequence 函数(集合处理) | seq.eq(value) | 返回一个谓词,用来判断传入的参数是否跟 value 相等,用于 filter 函数,如filter(seq,seq.eq(3)) 过滤返回等于3 的元素组成的集合 |
Sequence 函数(集合处理) | seq.neq(value) | 与 seq.eq 类似,返回判断不等于的谓词 |
Sequence 函数(集合处理) | seq.gt(value) | 返回判断大于 value 的谓词 |
Sequence 函数(集合处理) | seq.ge(value) | 返回判断大于等于 value 的谓词 |
Sequence 函数(集合处理) | seq.lt(value) | 返回判断小于 value 的谓词 |
Sequence 函数(集合处理) | seq.le(value) | 返回判断小于等于 value 的谓词 |
Sequence 函数(集合处理) | seq.nil() | 返回判断是否为 nil 的谓词 |
Sequence 函数(集合处理) | seq.exists() | 返回判断不为 nil 的谓词 |
Sequence 函数(集合处理) | seq.and(p1, p2, p3, …) | 组合多个谓词函数,返回一个新的谓词函数,当今仅当 p1、p2、p3 …等所有函数都返回 true 的时候,新函数返回 true |
Sequence 函数(集合处理) | seq.or(p1, p2, p3, …) | 组合多个谓词函数,返回一个新的谓词函数,当 p1, p2, p3… 其中一个返回 true 的时候,新函数立即返回 true,否则返回 false。 |
Sequence 函数(集合处理) | seq.min(coll) | 返回集合中的最小元素,要求集合元素可比较(实现 Comprable 接口),比较规则遵循 aviator 规则。 |
Sequence 函数(集合处理) | seq.max(coll) | 返回集合中的最大元素,要求集合元素可比较(实现 Comprable 接口),比较规则遵循 aviator 规则。 |
模块加载 | load(path) | 加载 path 路径指定的模块,每次都将重新编译该模块文件并返回 exports |
模块加载 | require(path) | 加载 path 路径指定的模块,如果已经有缓存,将直接返回结果,否则将编译并缓存 exports |
基础数据类型
Google Aviator 表达式引擎是基于 JVM 去工作,所以语法和 JAVA 是高度相似的,但是又基于 JAVA 进行了精简,现在我们先了解一下Aviator 表达式引擎所支持的数据类型:数字型(整数和浮点数)、字符串型。
通过 type(x) 可以判断数据类型是什么。
数字型-整数-Long 类型:
数字型-整数-大整数(BigInt):
数字型-浮点数-double
数字型-高精度浮点数-Decimal
数字型-数字类型转换
- 单一类型参与的运算,结果仍然为该类型,比如整数和整数相除仍然是整数,double 和 double 运算结果还是 double。
- 多种类型参与的运算,按照下列顺序: long -> bigint -> decimal -> double 自动提升,比如 long 和 bigint 运算结果为 bigint, long 和 decimal 运算结果为 decimal,任何类型和 double 一起运算结果为 double
字符串型-数字类型转换
- "hello world"
- 'hello world'
- "a" 或者 'a'
正则表达式
Aviator 的正则表达式使用与 JAVA 完全相同,示例:/^(.*).av$/
运算符
幂运算
从 5.1.3 开始,AviatorScript 引入幂运算符 ** ,原来使用 math.pow(a, b) 的代码都可以写成 a**b ,幂运算符的优先级较高,在单目运算符之上。幂运算的基本规则是:
- 基数和指数都为 long ,并且指数为正数,结果为 long
- 基数和指数都为 long,并且指数为负数,结果为 double
- 基数为 decimal,指数取整数部分(int value),等价于 BigDecimal#pow(int) 。
- 基数为 bigint,指数取整数部分(int value),等价于 BigInteger#pow(int) 。
- 基数或者指数任一为 double,结果为 double
位运算
我们还没有介绍的是数字的位运算,位运算仅支持整数 long,跟 Java 保持一致:
运算符优先级
完整的运算符优先级的优先顺序如下表,基本跟 java 保持一致,除了特别引入的正则匹配(记住这个优先级没有太大必要,推荐任何情况都使用括号来明确优先级):
优先级 | 运算符 | 结合性 | 备注 |
---|---|---|---|
1 | ( ) [ ] . | 从左到右 | 括号,数组,小数点 |
2 | ** | 从左到右 | |
3 | ! ~ | 从右到左 | |
4 | * / % | 从左到右 | |
5 | + – | 从左到右 | |
6 | << >> >>> | 从左到右 | |
7 | < <= > >= | 从左到右 | |
8 | == != | 从左到右 | |
9 | & | ! | 从左到右 | 与或非 |
10 | && | 从左到右 | |
11 | ? : | 从左到右 | |
12 | = =~ | 从右到左 |
三元运算符
String expression = "name != nil ? ('hello, ' + name):'who are u?'";
自定义运算符
前面我们已经看了运算符重载的例子,比如加减乘除,可以用于 long/double/bigint/decimal 等多种数字类型,这就是一个重载,比如加号 + 可以用于数字,也可以用于拼接字符串,这又是一个重载。
你也可以自定义任意运算符的行为,比如我们想将整数相除的结果修改为浮点数,可以:
package com.googlecode.aviator.example;
import java.util.Map;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.lexer.token.OperatorType;
import com.googlecode.aviator.runtime.function.AbstractFunction;
import com.googlecode.aviator.runtime.function.FunctionUtils;
import com.googlecode.aviator.runtime.type.AviatorDouble;
import com.googlecode.aviator.runtime.type.AviatorObject;
import com.googlecode.aviator.runtime.type.AviatorType;
/**
* An example to demo custom division operator.
*
* @author dennis(killme2008@gmail.com)
*
*/
public class CustomDivideExample {
public static void main(final String[] args) {
AviatorEvaluator.getInstance().addOpFunction(OperatorType.DIV, new AbstractFunction() {
@Override
public AviatorObject call(final Map<String, Object> env, final AviatorObject arg1,
final AviatorObject arg2) {
if (arg1.getAviatorType() == AviatorType.Long
&& arg2.getAviatorType() == AviatorType.Long) {
// If arg1 and arg2 are all long type.
// Cast arg2 into double and divided by arg1.
double d = FunctionUtils.getNumberValue(arg1, env).longValue()
/ FunctionUtils.getNumberValue(arg2, env).doubleValue();
return AviatorDouble.valueOf(d);
} else {
// Otherwise, call aviatorscript's div function.
return arg1.div(arg2, env);
}
}
@Override
public String getName() {
return OperatorType.DIV.getToken();
}
});
System.out.println(AviatorEvaluator.execute("1/2"));
}
}
通过 AviatorEvaluatorInstance#addOpFunction(opType, function) 就可以自定义运算符的行为,这个例子中如果我们发现 arg1/arg2 的类型都是 long ,那么我们就将 arg2 转成 double 并计算结果,结果需要包装成 AviatorDouble 类型返回;如果不是,我们继续调用原来的除法。所有的运算符都可以在 OperatorType 找到,你都可以自定义他的行为。
从这个例子中我们也可以看出 AviatorScript 中的所有类型都是 AviatorObject 的子类,他们包括:
可以参见 AviatorType 这个枚举类。所有运算符都实现为 AviatorObject 的一个方法,比如加法就是 add(arg2, env) ,减法就是 sub 等等,具体见 AviatorObject 文档。比较运算符的实现是基于 compare(arg2, env) 方法返回的结果,如果为 0 ,表示相等,大于返回正数,小于返回负数。
变量
基础变量使用
Google Aviator 支持定义变量,如果使用到的变量没有默认赋值,会为 nil 值(等于 JAVA 中的 null)。变量赋值和使用示例如下:
- 赋值为空(在 Java 中, null 只能参与等于和不等于的比较运算,而在 Aviator Script 中, nil 可以参与所有的比较运算符,只是规定任何类型都比nil大除了nil本身):a = nil;
- 赋值为数字:a = 98;
- 判空:a == nil
- 判不为空:a != nil
变量语法糖
Aviator 有个方便用户使用变量的语法糖, 当你要访问变量a中的某个属性b, 那么你可以通过a.b访问到, 更进一步, a.b.c将访问变量a的b属性中的c属性值, 推广开来也就是说 Aviator 可以将变量声明为嵌套访问的形式。
这个实现的基础是 commons-beanutils,基于反射实现。但是 AviatorScript 也做了优化,如果访问路径上的每一级变量都是 Map,会直接调用 Map#get(key) 访问,避免反射调用。
引用变量
对于深度嵌套并且同时有数组的变量访问,例如 foo.bars[1].name,从 3.1.0 版本开始, aviator 通过引用变量来支持(quote variable)。
从 5.2 版本开始可以直接访问 Java 类的静态变量,比如 `Math.PI`
多行表达式和 return
看这样一个例子:
let a = 1;
let b = 2;
c = a + b;
整个脚本的返回结果默认是最后一个表达式的结果。但是这里需要注意的是,加上分号后,整个表达式的结果将固定为 nil,因此如果你执行上面的脚本,并打印结果,一定是 null,而不是 c 的值。如果你想返回表达式的值,而不是为 nil,最后一个表达式不要加分号,如下:
let a = 1;
let b = 2;
c = a + b
这时候再执行 execute 将返回表达式 c = a +b 的值,赋值语句的结果即为右值,也就是 3。在 Aviator Script 中任何表达式都有一个值,加上分号后就是丢弃该值固定为 nil。除了不加分号来返回之外,你也可以用 return 语句来指定返回:
let a = 1;
let b = 2;
c = a + b;
return c;
注意, return 语句就必须加上分号才是完整的一条语句,否则将报语法错误
return 也用于提前返回,结合条件语句可以做更复杂的逻辑判断:
if a < b {
return "a is less than b.";
}
return a – b;
use 语句引用 Java 类
从 5.2 开始,aviatorscript 支持 use 语句,类似 java 里的 import 语句,可以导入 java 类到当前命名空间,减少在 new 或者 try…catch 等语句里写完整报名的累赘方式。 use 语句的使用方式多种,最简单的情况是导入单个 Java 类:
use java.util.Date;
let d = new Date();
p(type(d));
p(d);
use 包名.类名 就可以导入任意一个类到当前上下文。
如果要导入某个包下面的任意类,可以用通配符 *
甚至可以把防并发的包引入,做防并发处理。
条件语句
AviatorScript 中的条件语句和其他语言没有太大区别。if 接受一个布尔表达式,如果其值为 true 就执行后续的代码块。如果为 false ,可以带上 else 语句执行其中的代码块,代码块都是以大括号包起来(请注意,代码块都必须用大括号包起来,哪怕是单行语句,这跟 java 是不一样的):
if(false) {
println("in if body");
} else {
println("in else body");
}
if 后面连着的表达式的括号是可以忽略,上面的例子可以改写成:
if false { println("in if body"); } else { println("in else body"); }
嵌套结构应该直接使用 `elsif` 语句,类似 Java 中的 `else if` ,比如我们写一个猜数字的例子:
let a = rand(1100);
if a > 1000 {
println("a is greater than 1000.");
} elsif a > 100 {
println("a is greater than 100.");
} elsif a > 10 {
println("a is greater than 10.");
} else {
println("a is less than 10 ");
}
println("a is " + a + ".");
同样,同样 elsif 对应的判断语句的括号也是可以忽略的。
循环语句
循环语句通常用于遍历一个集合,或者重复执行若干指令,直到满足某个条件等等。 AviatorScript 支持 for 和 while 两种循环语句。
for 循环
示例如下
for i in range(0, 10) {
println(i);
}
其中 range(start, end) 函数用于创建一个 [start, end) 区间的整数集合,在迭代过程中,将 i 绑定到集合中的每个元素上,然后执行 {…} 里的代码块。大括号也是必需的,不能因为代码块是单行语句而忽略,这跟 java 是不同的。
range 函数还可以接受第三个参数,表示递增的 step 大小(不传默认 step 就是 1,步长的概念),比如我们可以打印 0 到 9 之间的偶数:
for i in range(0, 10, 2) {
println(i);
}
for .. in 可以用于任何集合结构,比如数组、 java.util.List 、 java.util.Map 等等:
let m = seq.map("a", 1, "b", 2, "c", 3);
for x in m {
println(x.key + "=" + x.value);
}
let list = seq.list(1, 2, 3, 4, 5, 6, 7, 8, 9);
let sum = 0;
for x in list {
sum = sum + x;
}
println("sum of list is "+ sum);
这里 m 是一个 HashMap ,通过 seq.map 函数创建,里面是三个键值对 a=1, b=2, c=3 ,我们也可以通过 for…in 语句来遍历,并且通过 x.key 和 x.value 来访问每一对的键值。list 就是一个 1 到 9 整数组成的 List ,我们利用 for 语句迭代累计它们的和 sum 并打印。
如果你想在执行代码块中途跳过剩余代码,继续下个迭代,可以用 continue。同样,如果想中途跳出迭代,你可以用 break。return 有类似 break 的效果,也可以从循环中跳出,但是它会将整个脚本(或者函数)中断执行并返回,而不仅仅是跳出循环。
while 循环
while 循环本质上是条件语句和循环的结合,当满足一定条件下,不停地执行一段代码块,直到条件变为否定。
let sum = 1;
while sum < 1000 {
sum = sum + sum;
}
println(sum);
while 后面跟 if 一样跟着一个布尔表达式,括号同样可以省略,但是代码块必须用大括号包围起来,这跟 if/for 也是一样的。同样, while 也可以用 break 和 continue 语句。使用 while true 来无限循环。return/continue 的使用和 for 类似,不再赘述。循环语句也可以多层嵌套,这跟其他语言都没有什么两样。
自定义函数和调用 Java 方法
如果你想在 AviatorScript 中调用 Java 方法,除了内置的函数库之外,你还可以通过下列方式来实现:
- 自定义函数
- 自动导入 java 类方法
- FunctionMissing 机制
这里主要介绍自定义函数
自定义函数
可以通过 java 代码实现并往引擎中注入自定义函数,在 AviatorScript 中就可以使用,事实上所有的内置函数也是通过同样的方式实现的:
public class TestAviator {
public static void main(String[] args) {
//注册函数
AviatorEvaluator.addFunction(new AddFunction());
System.out.println(AviatorEvaluator.execute("add(1, 2)")); // 3.0
System.out.println(AviatorEvaluator.execute("add(add(1, 2), 100)")); // 103.0
}
}
class AddFunction extends AbstractFunction {
@Override
public AviatorObject call(Map<String, Object> env,
AviatorObject arg1, AviatorObject arg2) {
Number left = FunctionUtils.getNumberValue(arg1, env);
Number right = FunctionUtils.getNumberValue(arg2, env);
return new AviatorDouble(left.doubleValue() + right.doubleValue());
}
public String getName() {
return "add";
}
}
所有的函数都实现了 AviatorFunction 接口。它有一系列 call 方法,根据参数个数不同而重载,其中 getName() 返回方法名。一般来说都推荐继承 com.googlecode.aviator.runtime.function.AbstractFunction ,并覆写对应参数个数的方法即可,例如上面例子中定义了 add 方法,它接受两个参数。第一个参数是当前执行的上下文,arg1 和 arg2 分别表示从左到右的两个参数,最终结果是另一个 AviatorObject ,这个实现中是将两个参数相加,返回浮点结果 AviatorDouble 。
注意,哪怕结果为 null,也必须返回 AviatorNil.NIL 表示结果为 null,而不是直接返回 java 的 null。
自定义可变参数函数
要实现可变参数的函数,如果是直接继承 AbstractFunction ,要实现一系列的 call 方法,未免太繁琐了,因此 AviatorScript 还提供了 AbstractVariadicFunction ,可以更方便地实现可变参数函数,比如内置的 tuple(x, y, z,…) 创建 Object 数组的函数就是基于它实现:
import java.util.Map;
import com.googlecode.aviator.runtime.function.AbstractVariadicFunction;
import com.googlecode.aviator.runtime.type.AviatorObject;
import com.googlecode.aviator.runtime.type.AviatorRuntimeJavaType;
/**
* tuple(x,y,z, …) function to return an object array.
*
* @author dennis
* @since 4.0.0
*
*/
public class TupleFunction extends AbstractVariadicFunction {
private static final long serialVersionUID = -7377110880312266008L;
@Override
public String getName() {
return "tuple";
}
@Override
public AviatorObject variadicCall(Map<String, Object> env, AviatorObject… args) {
Object[] tuple = new Object[args.length];
for (int i = 0; i < args.length; i++) {
tuple[i] = args[i].getValue(env);
}
return AviatorRuntimeJavaType.valueOf(tuple);
}
}
核心就是实现 variadicCall(Map<String, Object> env, AviatorObject… args) 方法,其中的 args 就是可变的参数列表。
数组和集合
count 可以获取数组长度:
println("count of t: "+ count(t));
遍历数组
let a = seq.array(int, 1, 2, 3.3, 4);
for x in a {
println(x);
}
判断元素是否存在
对于数组、List 和 Set 来说,判断某个元素是否存在都应该用 include(coll, element) 函数,对于数组和 List 来说,这个函数的时间复杂度是 O(n),因为要遍历整个数组或链表;对于 Set 来说是 O(1) 时间复杂度,直接调用用了 Set#contains 方法。
for i in range(0, 3) {
assert(include(list, i));
assert(include(set, i));
}
assert(!include(list, 5));
assert(!include(set, 5));
对于 map 来说,如果是判断 key 是否存在,需要用 seq.contains_key(coll, key) :
for i in range(0, 3) {
assert(seq.contains_key(map, i));
}
assert(!seq.contains_key(map, 5));
遍历集合
遍历集合和数组的方式一样,同样通过 for..in 语句:
## Iterate the collection by for..in loop
println("list elements:");
for x in list {
println(x);
}
println("set elements:");
for x in set {
println(x);
}
println("map elements:");
for x in map {
println(x.key + "=" + x.value);
}
对于 map 来说迭代循环中的元素就是 Map.Entry 对象,可以通过 key 和 value 属性来访问键和值。
删除元素 seq.remove
删除元素也是常见的需求,可以用 seq.remove(coll, element) ,对于 List/Set 和 Map 都是如此,如果是 Map,传入的应该是 key:
## remove elements
assert(list == seq.remove(list, 2));
assert(list == seq.remove(list, 4));
assert(set == seq.remove(set, 1));
assert(map == seq.remove(map, 0));
println("list: " + list);
println("set: " + set);
println("map: " + map);
删除不存在的元素不产生影响。 seq.remove 返回的是删除后的集合对象。
操作 sequence 的高阶函数
对于 Sequence 的抽象, AviatorScript 也提供了一套高阶函数来方便地对集合做转换、过滤、查询以及聚合,我们将一一介绍。这些函数的规则都是将 sequence 作为第一个参数。
Count
count(seq) 函数用于获取 seq 里的集合元素,它将尽量在 O(1) 的时间复杂度内返回结果,最差情况下退化成 O(n):
## count
println("count of array: " + count(a));
println("count of range: " + count(r));
println("count of set: " + count(s));
println("count of map: " + count(m));
println("count of list: " + count(n));
is_empty
is_empty 用于返回集合是否为空, is_empty(nil) 返回 true :
println("is_empty(array): " + is_empty(a));
println("is_empty(seq.list()): " + is_empty(seq.list()));
println("is_empty(nil): " + is_empty(nil));
include
include(seq, x) 用于判断元素 x 是否在 seq 内,对于 Set 是 O(1) 的时间复杂度,其他是 O(n):
## include
println("array has 3: " + include(a, 3));
println("map has an entry ('b', 2): " + include(m, seq.entry("b", 2)));
println("range has 10: " + include(r, 10));
参考资料
- https://www.yuque.com/boyan-avfmj/aviatorscript/cpow90
- https://juejin.cn/post/6992213605861523493
- https://juejin.cn/post/6992586914373042207?searchId=202308142048456CF580127A23B5275785
- https://juejin.cn/post/6992956846591983647
- https://juejin.cn/post/6993319031470030884
- https://juejin.cn/post/6993688391816577061
- https://juejin.cn/post/6994068942272610340
- https://juejin.cn/post/6994432429855342623
- https://juejin.cn/post/6994803531064557575
- https://juejin.cn/post/6995181281029914654
- https://juejin.cn/post/6995558411609833486
- https://juejin.cn/post/6996168805709774878
- https://juejin.cn/post/6996170437315002381
- https://juejin.cn/post/6996654434096775205
- https://juejin.cn/post/6997008934389153806
- https://juejin.cn/post/6997369285593006116
- https://juejin.cn/post/6997778225124507678
- https://juejin.cn/post/6998137825489387556
- https://juejin.cn/post/6998512130399928357
- https://juejin.cn/post/6998884618598350856
- https://juejin.cn/post/6999255830985965575
- https://juejin.cn/post/6999619488635158536
- https://juejin.cn/post/7000013860564369444
- https://juejin.cn/post/7000370788998053896
- https://juejin.cn/post/7000742854700040222
- https://juejin.cn/post/7001111119246917663
- https://juejin.cn/post/7001489301611479077
- https://juejin.cn/post/7002235897873694757
- https://juejin.cn/post/7002606430519853069
作者:a1124544556