Java使用Aviator表达式 学习记录(十二)

971 阅读4分钟

这是我参与8月更文挑战的第14天,活动详情查看:8月更文挑战

函数

函数的定义和调用

我们通过 fn 语法来定义一个命名函数:

## examples/function.av

fn add(x, y) {
  return x + y;
}

three = add(1, 2);
println(three);

我们定义了一个函数 add ,它接受两个参数 x 和 y ,返回两者相加的结果。注意到,你并不需要定义参数的类型以及返回值的类型,因为 AviatorScript 是动态类型系统,会根据你实际传入和返回的类型自动转换。所以我们也可以用字符串来调用 add :

## examples/function.av
s = add('hello', ' world');
println(s);

打印 hello world 。调用函数就是 函数名(参数1,参数2....) 的方式,这一点跟大多数编程语言保持一致。

函数返回值

函数的返回值,可以通过 return 语句来直接返回,它可以带一个返回值,也可以不带。比如我们限定 add 方法只允许执行数字:

## examples/function_return.av

fn add(x, y) {
  if type(x) != 'long' || type(y) != 'long' {
    throw "unsupported type";
  }
  x + y
}

println(add(1, 2));
println(add('hello', ' world'));

如果 x 或者 y 不是 long,我们通过 throw 抛出一个异常(具体见异常处理一节),因此这里将打印:

3
Exception in thread "main" com.googlecode.aviator.exception.StandardError: unsupported type
	at com.googlecode.aviator.runtime.function.internal.ThrowFunction.call(ThrowFunction.java:30)
	at Script_1598449868044_2/1689843956.execute0(Unknown Source)
	at com.googlecode.aviator.ClassExpression.executeDirectly(ClassExpression.java:65)
	at Lambda_1598449868044_1.call(Unknown Source)
	at Script_1598449868039_1/1590550415.execute0(Unknown Source)
	at com.googlecode.aviator.ClassExpression.executeDirectly(ClassExpression.java:65)
	at Lambda_1598449868039_0.call(Unknown Source)
	at com.googlecode.aviator.RuntimeFunctionDelegator.call(RuntimeFunctionDelegator.java:63)
	at Script_1598449868034_0/1338823963.execute0(Unknown Source)
	at com.googlecode.aviator.ClassExpression.executeDirectly(ClassExpression.java:65)
	at com.googlecode.aviator.BaseExpression.execute(BaseExpression.java:136)
	at com.googlecode.aviator.Main.main(Main.java:42)

细心的朋友肯定注意到,这里 x +y 没有用到 return ,而是一个普通的表达式,并且没有加上分号 ; 。AviatorScript 如果没有明确的 return  语句,函数默认返回最后一个表达式的值,在这个例子中就是 x+y 的值。请注意,不能加上分号,如果加上, x+y; 的值是 nil。这个规则是向 rust 学习的。关于多行表达式的返回值请参考 3.6 节

这个例子我们也演示了函数的连续调用 println(add(1, 2)); ,连续调用了 add 和 println,基本的规则是先对每个函数的参数求值,然后调用函数并返回结果,这里 add(1, 2) 的结果又作为 println 的参数继续执行。

函数重载

从 5.2 开始,aviatorscript 支持参数个数的函数重载,举个例子:

## examples/function_overload.av

fn join(s1) {
  "#{s1}"
}
fn join(s1, s2) {
  "#{s1}#{s2}"
}

fn join(s1, s2, s3) {
 "#{s1}#{s2}#{s3}"
}

p(join("hello"));
p(join("hello", " world"));
p(join("hello", " world", ", aviator"));

join 定义了三个版本的分支函数,分别接受 1 个、2 个和 3 个参数,返回拼接后的字符串,然后我们尝试测试了调用它们,输出:

hello
hello world
hello world, aviator
null

同名的函数按照参数个数来重载,不支持类型重载,后面定义的相同参数的分支将覆盖原来的,比如我们继续给 join 重新定义一个参数的分支,让他返回 nil:

## redefined join(s)
fn join(s1) {
  nil
}

p(join("hello"));
p(join("hello", " world"));
p(join("hello", " world", ", aviator"));

再次执行将输出:

null
hello world
hello world, aviator

单参数的分支将返回 nil 并打印为 null。

不定参数

同样, 从 5.2 版本开始,aviatorscript 也支持了不定参数个数的函数定义,跟 java 的要求类似,也要求可变参数只能出现在参数列表的最后一个位置,并且用 & 作为前缀,比如我们定义一个使用间隔符拼接字符串的 join 函数:

## examples/function_varargs.av

fn join(sep, &args) {
  let s = "";
  let is_first = true;
  for arg in args {
    if is_first {
      s = s + arg;
      is_first = false;
    }else {
      s = s + sep + arg;
    }
  }

  return s;
}

p(join(" ", "a", "b", "c"));
p(join(",", "a", "b", "c", "d"));
p(join(",", "a"));

第一个参数是间隔符 sep ,第二个参数是可变的参数 args ,通过符号 & 来表明它是一个可变参数,最终会将可变的参数收集成 List 并传入 join  函数,然后我们通过 for 循环遍历这些参数并做字符串拼接:

a b c
a,b,c,d
a

Unpacking Arguments(参数解包)

很多时候,你想传入的参数可能是一个数组,而函数接受的却是拆开的一个一个的参数,这个时候,就可以用上 unpacking arguments,语法设计和 python 类似:

## examples/unpacking_args.av

fn add(a, b) {
 a + b
}

let list = seq.list(1, 2);
p(add(*list));

add 函数接受两个参数 a 和 b,而 list 是一个链表,如果想调用 add,原始的方式是自己拆开 add(list[0], list[1]) ,特别的不方便,通过 unpacking arguments 支持,只要给 list 前面加上 * 号,也就是 *list 就可以自动帮你“展开”。

unpacking 可以发生在参数的任何位置:

fn test(a, b, c, d) {
  a * b + c * d
}
let a = tuple(1, 2);
let list = seq.list(3, 4);

p(test(*a, *list));

四个参数,通过两个 sequence 解开来填充。计算结果为 1*2 + 3*4 等于 14 。

可变参数本质上也是数组,因此也可以用 unpacking arguments 的方式来调用:

fn average(&args) {
    return sum(*args) / count(args);
}

fn sum(&args) {
    s = 0.0;
    for arg in args {
        s = s + arg;
    }
    return s;
}

p(average(1, 2, 3, 4));

sum 接受一个可变参数数组,因此在 average 调用 sum 的时候,需要 unpacking,否则 sum 接收到的是一个数组组成的数组。上述结果为 2.5 。