跳到主要内容

函数

基础使用

函数用来封装可复用的功能。如果没有函数,一段特定的操作过程用几次就要重复写几次,而使用函数则只需写下函数名和一些简短的信息。

函数通常由函数名称、输入参数、输出参数和函数体组成,广义上就是一台接受输入并经过计算后返回计算结果的机器。

下面是 nature 中的函数声明语句中定义的语法,以基础的求和函数为例

// 定义
fn sum(int a, int b):int {
return a + b
}

// 调用
var s = sum(1, 2)

函数的名称是 sum,接受两个 int 类型的形式参数 a 和 b 并返回一个 int 类型,多个形式参数之间使用逗号分隔。 在函数声明中的输入参数和返回值参数的类型不可省略,也不可以使用类型推断关键字 var。

总结一下语法 fn ident(T ident [, T ident...])[:T] 函数的返回值类型声明在括号右侧,使用冒号隔开。仅支持单返回值,仅需要填写类型不需要 ident 声明。

信息

return expr 退出函数的执行,并将 expr 作为返回计算的结果。

sum(1, 2) 是函数的调用语法,sum 为函数名称,1 和 2 称为实际参数,多个实际参数之间使用逗号分隔。

函数也可以作为表达式赋值给变量,调用方式与直接定义函数一样。

var f = fn(int a, int b):int {
return a + b
}


var s = f(1, 2)

💡 当函数作为表达式时,推荐省略函数的原本的名称,也就是所谓的匿名函数声明。这样可以避免混乱的名称。

可变参数

示例

fn sum(...[int] list):int {
var result = 0
for (k in list) { // 迭代语法
result += list[k] // list 类型通过 list[index] 的方式访问其中的值
}
return result
}

println(result(1, 2, 3, 4, 5))

输出如下

> ./main
15

... 语法(rest) 只允许在函数的最后一个参数中以 vec 类型收集调用者传递的参数,调用者传递的参数的类型需要与 ...[T] 中的 T 的类型匹配,或者能够隐式类型转换。

在调用函数时,可以通过 ... 语法对 vec 进行解构,解构语法同样只能用在最后一个参数中,且必须有对应的可变参数接应。示例

fn sprintf(string fmt, ...[any] args):string {}

fn printf(string fmt, ...[any] args) {
var str = sprintf(fmt, ...args)
print(str)
}

多返回值?

上面说函数只支持单个返回值,但是可以基于 tuple 解构赋值实现近似多返回值的效果,用法如下

fn buy(int amount):(string, int) {
amount -= 10
return ('apple', amount)
}

var (product, balance) = buy(100)

println(balance, ' ', product)

示例中函数返回值通过 (string, int) 声明了一个 tuple 类型。 return ("apple", amount) 则是创建了一个 tuple 实例返回,这并不违反单返回值的情况。

var (product, balance) 这就是一种新的语法形式,称为 tuple 解构赋值。这是为了能够模拟函数多返回值而开发的一种语法,在解构赋值中必须使用 var 进行自动多类型推导,而不能通过类型类型,因为这很容易造成误导,且无法不美观,所以语法上禁止了这种情况。

后续的部分将会更加详细的说明 tuple 的使用方法。