目录
[1.1 函数基本定义语法](#1.1 函数基本定义语法)
[1.2 两种参数类型:非命名 & 命名](#1.2 两种参数类型:非命名 & 命名)
[1.3 函数调用的核心要求](#1.3 函数调用的核心要求)
[2.1 函数是一等公民](#2.1 函数是一等公民)
[2.2 嵌套函数](#2.2 嵌套函数)
[2.3 匿名函数:Lambda 表达式](#2.3 匿名函数:Lambda 表达式)
[2.3.1 Lambda 基本语法](#2.3.1 Lambda 基本语法)
[2.3.2 Lambda 代码示例](#2.3.2 Lambda 代码示例)
[2.3.3 尾随 Lambda(语法糖)](#2.3.3 尾随 Lambda(语法糖))
在仓颉语言中,函数是一等公民,不仅支持常规的定义与调用,还具备嵌套、Lambda 表达式、闭包、操作符重载等高级特性,同时提供了 const 函数编译期求值、函数调用语法糖等实用能力,适配全场景开发的灵活需求。
1.函数基础:定义与调用
仓颉使用关键字func 来表示函数定义的开始,func 之后依次是函数名、参数列表【支持非命名参数 、命名参数(可带默认值)】、可选的函数返回值类型、函数体。
1.1 函数基本定义语法
java
// 完整语法:func 函数名(参数列表): 返回值类型 { 函数体 }
func add(a: Int64, b: Int64): Int64 {
return a + b // 显式return返回值
}
// 省略return:函数体最后一项为表达式,自动作为返回值
func sub(a: Int64, b: Int64): Int64 {
a - b
}
// 省略返回值类型:编译器自动推导为Int64
func mul(a: Int64, b: Int64) {
a * b
}
// 无参数、无返回值(默认返回Unit类型)
func sayHello() {
println("Hello Cangjie!")
}
- 仓颉函数的返回值类型 为可选 配置,编译器会根据**
return表达式或函数体最后一项**自动推导 - 无返回值的函数默认返回
Unit类型,等价于 Java 的void,但Unit是实际的类型,唯一实例为() - 函数体中若显式写
return,其表达式类型必须与返回值类型匹配;若省略return,函数体最后一项需为对应类型的表达式
1.2 两种参数类型:非命名 & 命名
一个函数可以拥有 0 个或多个参数,这些参数均定义在函数的参数列表中。根据函数调用时是否需要给定参数名 ,可以将参数列表中的参数分为两类:非命名参数和命名参数。其中命名参数支持设置默认值。
核心规则:
- 非命名参数 :语法
p: T,调用时直接传值,无需指定参数名; - 命名参数 :语法
p!: T,调用时需写p: 值,仅命名参数可设置默认值**p!: T = 表达式**; - 混合参数 :非命名参数必须在前,命名参数在后,不可交替。
java
// 混合参数:非命名参数a在前,命名参数b/c在后(c带默认值)
func calc(a: Int64, b!: Int64, c!: Int64 = 10): Int64 {
a + b * c
}
main(): Int64 {
// 调用1:全传参,命名参数指定名称
let res1 = calc(2, b: 3, c: 5) // 2+3*5=17
// 调用2:命名参数c使用默认值10
let res2 = calc(2, b: 3) // 2+3*10=32
// 调用3:命名参数顺序可任意调整
let res3 = calc(2, c: 2, b: 4) //2+4*2=10
println("res1:${res1}, res2:${res2}, res3:${res3}")
return 0
}
1.3 函数调用的核心要求
- 实参类型必须是形参类型的子类型,编译期严格校验;
- 命名参数调用时必须指定参数名,无默认值的命名参数不可省略;
- 函数参数为不可变变量 ,函数体内不能对形参赋值(如
a = a + 1会编译报错)。
2.函数的进阶特性
2.1 函数是一等公民
仓颉编程语言中,函数是一等公民(first-class citizens),可以作为函数的参数或返回值,也可以赋值给变量。因此函数本身也有类型,称之为函数类型。
函数类型 由函数的参数类型 和返回类型 组成,参数类型和返回类型之间使用**->** 连接。语法为**(参数类型列表) -> 返回值类型**。
java
// 定义普通函数
func add(a: Int64, b: Int64): Int64 {
a + b
}
// 1. 函数赋值给变量:变量类型为函数类型
let f1: (Int64, Int64) -> Int64 = add
// 2. 函数作为参数:形参类型为函数类型
func callFunc(fun: (Int64, Int64) -> Int64, x: Int64, y: Int64): Int64 {
fun(x, y)
}
// 3. 函数作为返回值:返回值类型为函数类型
func returnFunc(): (Int64, Int64) -> Int64 {
add
}
main(): Int64 {
let res1 = f1(10, 20) // 30
let res2 = callFunc(add, 100, 200) // 300
let f2 = returnFunc()
let res3 = f2(1, 2) //3
println("res1:${res1}, res2:${res2}, res3:${res3}")
return 0
}
- 函数类型支持类型参数名 ,如
(x: Int64, y: Int64) -> Int64,但需统一写或统一不写,不可交替; - 函数名本身是表达式,其类型为对应的函数类型,重载函数直接赋值会产生歧义,需显式指定变量类型。
2.2 嵌套函数
定义在函数体内的函数称为嵌套函数 ,其作用域仅限于外部函数,可访问外部函数的变量 / 参数,也可被外部函数返回(结合闭包使用)。
核心特性
- 作用域隔离 :嵌套函数的作用域仅限于其所在的外部函数 。嵌套函数可以访问外部函数的变量和参数,但外部函数不能直接访问嵌套函数的内部变量。
- 生命周期绑定 :每次外部函数调用 时,嵌套函数被创建;外部函数执行完毕后,嵌套函数通常被销毁,除非通过返回或闭包被外部引用。
- 可被返回:嵌套函数可作为外部函数的返回值,脱离原作用域后通过闭包继续生效。
java
func outerFunc(base: Int64): (Int64) -> Int64 {
// 嵌套函数:访问外部函数的参数base
func innerFunc(num: Int64): Int64 {
num + base
}
// 返回嵌套函数
return innerFunc
}
main(): Int64 {
// 调用外部函数,base=10,得到嵌套函数实例
let f = outerFunc(10)
// 调用嵌套函数,仍可访问base=10
println(f(5)) // 15
println(f(8)) // 18
return 0
}
2.3 匿名函数:Lambda 表达式
Lambda 表达式是无函数名的匿名函数,用于快速定义简短的函数逻辑,可直接调用、赋值给变量、作为函数参数 / 返回值,是仓颉简化代码的核心特性之一。
2.3.1 Lambda 基本语法
{ 参数列表: 参数类型 => 函数体 }
// 简化:参数类型可省略(编译器自动推导),无参数写{ => 函数体 }
核心规则
- 无论有无参数,=>不可省略(尾随 Lambda除外)
- 不支持显式声明返回值类型,由上下文自动推导
- 可立即调用,也可赋值给变量后调用
2.3.2 Lambda 代码示例
java
main(): Int64 {
// 1. 带参数的Lambda,显式指定类型
let add = { a: Int64, b: Int64 => a + b }
println(add(3, 5)) // 8
// 2. 无参数的Lambda
let sayHi = { => println("Hi Lambda!") }
sayHi() // Hi Lambda!
// 3. Lambda立即调用
let res = { x: Int64, y: Int64 => x * y }(4, 6)
println(res) //24
// 4. Lambda作为函数参数(结合函数一等公民特性)
func compute(fun: (Int64) -> Int64, num: Int64) {
return fun(num)
}
let res1 = compute({ n: Int64 => n * n }, 8)
println(res1) //64
return 0
}
2.3.3 尾随 Lambda(语法糖)
当函数最后一个形参是函数类型 ,且实参是 Lambda 时,可将 Lambda 移到函数调用的圆括号外 ,称为尾随 Lambda ,此时可省略 =>,让代码更简洁。
java
// 定义函数:最后一个参数为函数类型
func doSomething(a: Int64, fn: (Int64) -> Int64): Int64 {
fn(a)
}
main(): Int64 {
// 普通调用
let res1 = doSomething(5, { n: Int64 => n * 2 }) //10
// 尾随Lambda:可以把Lambda移出括号,Lambda参数类型可省略(类型推断)
let res2 = doSomething(5) { n => n * 2 } //10
// 极致简化:函数只有一个参数,且该参数是函数类型
func onlyLambda(fn: () -> Unit) { fn() } // 接收一个无参数、无返回值的函数并在函数体中执行
onlyLambda { println("尾随Lambda极致简化") }
println("res1:${res1}, res2:${res2}")
return 0
}
输出结果:
