仓颉—函数

内容纲要

@[toc]

函数

函数定义

仓颉使用关键字func表示函数定义的开始,func之后依次是函数名、参数列表、可选的函数返回值类型、函数体。其中,函数名可以是任意合法标识符,参数列表定义在一对圆括号内,参数列表和函数返回值类型之间使用冒号分隔,函数体定义在花括号内。

func add(a: Int64, b: Int64): Int64
{
    return a + b
}

定义了一个名为add的函数,参数列表由两个Int64类型参数ab组成,函数返回值类型为Int64,函数体中将ab相加并返回。

参数列表

一个函数可以拥有 0 个或多个参数,这些参数均定义在函数的参数列表中。根据函数调用时是否需要给定参数名,可以将参数列表中的参数分为两类:非命名参数和命名参数

非命名参数的定义方式是p: T,其中p表示参数名,T表示类型,参数名和其类型间使用冒号连接。

命名参数的定义方式是p!: T,与非命名参数的不同是在参数名p后多了一个!。可将上例中add函数的两个非命名参数修改为命名参数:

func add(a!: Int64, b!: Int64): Int64
{
    return a + b
}

命名参数还可以设置默认值,通过p!: T = e方式将参数p的默认值设置为表达式e的值。例如,可以将上述add函数的两个参数的默认值都设置为1:

func add(a!: Int64 = 1, b!: Int64 = 1): Int64
{
    return a + b
}

注:只能为命名参数设置默认值,非命名参数不能设置默认值

参数列表可以同时定义非命名参数和命名参数,但非命名参数只能定义在命名参数之前。下例的定义是不合法的:

func add(a!: Int64, b: Int64): Int64
{
    return a + b
}

函数返回值类型

函数定义时,返回值类型是可选的,可以不定义,由编译器推导确定。

当显式定义了函数返回值类型时,就要求函数体的类型、函数体中所有return e表达式中e的类型是返回值类型的子类型。例如,对于上述add函数,显式定义其返回值类型为Int64,若将函数体中的return a + b修改为return (a, b),则会报错:

// Error: the type of the expression after return does not match the return type of the function
func add(a: Int64, b: Int64): Int64
{
    return (a, b)
}

在函数定义时若未显式定义返回值类型,编译器将根据函数体的类型及函数体中所有的return表达式共同推导函数的返回值类型:

func add(a: Int64, b: Int64)
{
    return a + b
}

注:返回值类型不是任何情况下都可被推导出,若推导失败,编译器会报错

函数体

func add(a: Int64, b: Int64)
{
    var r = 0
    r = a + b
    return r
}

在函数体的任意位置都可使用return表达
式终止函数的执行并返回。有两种形式:returnreturn expr(expr是一个表达式)。

对于return expr,要求expr的类型与函数定义中的返回值类型保持一致。对于return,其等价于return(),所以函数的返回值类型为Unit

func foo(): Unit
{
    add(1, 2)
    return
}

注:return表达式作为一个整体,其类型并不由后面跟随的表达式决定,而是Nothing类型。

在函数体内定义的变量属于局部变量的一种,作用域从其定义之后开始到函数体结束。

对于一个局部变量,允许在其外层作用域中定义同名变量,并且在此局部变量的作用域内,局部变量会”遮盖“外层作用域的同名变量:

let r = 0
func add(a: Int64, b: Int64)
{
    var r = 0
    r = a + b
    return r
}

函数体也是有类型的,是函数体内最后一”项“的类型:若最后一项为表达式,则函数体的类型是此表达式的类型,若为变量定义或函数声明,或函数体为空,则函数体的类型为Unit

func add(a: Int64, b: Int64): Int64
{
    a + b
}

上例中,因为函数体的最后一”项“为Int64类型的表达式,所以函数体的类型也是Int64,与函数定义的返回值类型相匹配。下例中函数体的最后一项是print函数调用,所以函数体的类型是Unit,同样与函数定义的返回值相匹配:

func foo(): Unit
{
    let s = "Hello"
    print(s)
}

函数调用

函数调用的形式为f(arg1, arg2, ..., argn)。其中,f是要调用的函数的名字,argnn个调用时的参数(实参),要求每个实参的类型必须对应参数类型的子类型。实参可以有 0 个或多个,当实参个数为 0 时,调用方式为f()

根据函数定义时参数是非命名参数还是命名参数的不同,函数调用时传实参的方式也有所不同:对于非命名参数,其对应的实参是一个表达式,对于命名参数,其对应的实参需要使用p: e的形式,其中p是命名参数的名字,e是表达式(传递给p的值)。

非命名参数调用:

func add(a: Int64, b: Int64)
{
    return a + b
}

func main()
{
    let x = 1
    let y = 2
    let r = add(x, y)
    println("The sum of x and y is ${r}")
}

命名参数调用:

func add(a: Int64, b!: Int64)
{
        return a + b
}

func main()
{
        let x = 1
        let y = 2
        let r = add(x, b: y)
        println("The sum of x and y is ${r}")
}

对于多个命名函数,调用时的传参顺序可以和定义时的参数顺序不同:

func add(a!: Int64, b!: Int64)
{
        return a + b
}

func main()
{
        let x = 1
        let y = 2
        let r = add(b: y, a: x)
        println("The sum of x and y is ${r}")
}

对于拥有默认值的命名参数,调用时若没有传实参,则此参数将使用默认值作为实参值。例如,下例中调用add函数时没有为参数b传实参,则参数b的值等于默认值2

func add(a: Int64, b!: Int64 = 2)
{
        return a + b
}

func main()
{
        let x = 1
        let r = add(x)
        println("The sum of x and y is ${r}")
}

对于有默认值的命名参数,调用时也可为其传递新的实参,此时命名参数的值等于新的实参的值。

func add(a: Int64, b!: Int64 = 2)
{
        return a + b
}

func main()
{
        let x = 1
        let r = add(x, b: 20)
        println("The sum of x and y is ${r}")
}

留下评论

您的电子邮箱地址不会被公开。