| name | cangjie-function |
| description | 仓颉语言函数。当需要了解仓颉语言的函数定义、调用、命名参数、默认值、函数类型、Lambda表达式、闭包、嵌套函数、函数重载、运算符重载、尾随Lambda、管道运算符(|>)、组合运算符(~>)、变长参数等特性时,应使用此 Skill。 |
仓颉语言函数 Skill
1. 函数定义
1.1 基本语法
func functionName(param1: Type1, param2: Type2): ReturnType {
// 函数体
}
- 关键字
func 开头
- 参数在
() 内,逗号分隔
- 返回类型在参数列表后的
: 之后(可选,编译器可推断)
- 函数体在
{} 内
1.2 参数列表
- 非命名参数:
p: T — 位置参数,调用时无标签
- 命名参数:
p!: T — 定义时使用 ! 后缀,调用时使用 p: value 形式(调用不需要写!)
- 带默认值的命名参数:
p!: T = expr — 仅命名参数可有默认值,使用 = 赋值
- 顺序规则:非命名参数须在命名参数之前,命名参数后不能跟非命名参数
- 不可变性:所有函数参数在函数体内不可变(不能重新赋值)
- 作用域:参数名从定义处到函数体结束有效。在函数体内重新定义同名变量会报错
- 命名参数完整示例:
// 函数定义,其中 indent 是命名参数,默认值为 2
func serializePretty(value: JsonValue, indent!: Int64 = 2): String { ... }
// 函数调用,命名参数 indent 使用前缀传值(注意不带 ! 符号)
serializePretty(value, indent: 4)
// 省略命名参数时使用默认值
serializePretty(value) // indent 使用默认值 2
1.3 返回类型
- 可显式声明或省略(编译器推断)
- 若已声明,所有
return e 表达式和函数体的最终表达式须为声明类型的子类型
- 若返回类型为
Unit,编译器自动在所有返回点插入 return ()
- 若推断失败,编译器报错
1.4 函数体
- 包含变量定义、表达式、以及可选的嵌套函数定义
return expr:终止执行,返回 expr(须匹配返回类型)
return:等价于 return (),须要求 Unit 返回类型
return 表达式的类型为 Nothing
- 函数体类型 = 最后一项的类型:表达式 → 该表达式类型;变量定义/函数声明/空 →
Unit
2. 函数调用
2.1 基本调用语法
f(arg1, arg2, ..., argn)
2.2 非命名参数调用
2.3 命名参数调用
- 使用
paramName: value 语法:add(b: y, a: x)
- 命名实参的顺序可以与定义顺序不同
2.4 默认值
- 若调用时未提供带默认值的命名参数,则使用默认值
- 若提供了新值,则覆盖默认值
3. 函数类型 / 一等公民
3.1 函数类型语法
(ParamType1, ParamType2, ...) -> ReturnType
-> 右结合
- 示例:
() -> Unit — 无参,返回 Unit
(Int64, Int64) -> Int64 — 两个 Int64 参数,返回 Int64
() -> (Int64, Int64) -> Int64 — 返回一个函数
3.2 类型参数名
- 函数类型可选包含参数名:
(name: String, price: Int64) -> Unit
- 须全部具名或全部不具名,不可混合
3.3 函数作为一等值
4. Lambda 表达式
4.1 Lambda 语法
{ p1: T1, p2: T2 =>
exprs
}
=> 分隔参数和函数体,不可省略(尾随 Lambda 除外)
- 函数体是 exprs(1~N 个表达式/定义),多个时各占一行
- Lambda 的值/类型 = 函数体最后一个表达式的值/类型
- 无参 Lambda:
{ => exprs }
- 参数类型可省略(当可从上下文推断时)
- 返回类型始终推断,不可显式声明
4.2 返回类型推断
- 从变量类型标注、函数参数类型或外围函数返回类型推断
- 若无上下文,从函数体最后一个表达式的类型推断(空体为
Unit)
4.3 Lambda 调用
- 立即调用:
{ a: Int64, b: Int64 => a + b }(1, 2)
- 通过变量调用:赋给变量,使用
variableName(args) 调用
5. 闭包
5.1 定义
闭包 = 函数/Lambda + 从其定义(词法)作用域捕获的变量
5.2 什么算作变量捕获
- 访问在函数/Lambda 外部定义的局部变量
- 在默认参数值中访问函数外部的变量
- 在类/结构体中,非成员函数/Lambda 访问实例成员或
this
5.3 什么不算作捕获
- 访问自身的局部变量或参数
- 访问全局变量或静态成员变量
- 实例成员函数中通过隐式
this 访问实例成员
5.4 捕获规则
- 被捕获的变量须在闭包定义处可见
- 被捕获的变量须在闭包定义前已初始化
- 若捕获的变量为引用类型,其可变实例成员可被修改
- 捕获
var 的闭包不能逃逸:不能赋给变量、返回、作为参数传递或作为独立表达式 — 只能直接调用
- 传递性捕获:若函数
f 调用捕获了 var 变量的函数 g(该 var 非 f 的局部变量),则 f 也被视为捕获了 var,不能逃逸
- 静态/全局
var 变量不算捕获 — 访问它们的函数仍为一等值
6. 嵌套函数
- 在其他函数体内定义的函数
- 作用域:仅在外围函数内可见
- 可访问外围函数的变量和参数
- 可在外围函数中调用或作为值返回
- 生命周期:每次外围函数调用时创建,外围函数结束时销毁(除非作为闭包返回/捕获)
7. 函数重载
7.1 重载规则
- 同名、不同参数数量或类型 → 有效重载
- 泛型函数:对齐类型参数名后,若非泛型部分不同 → 重载;否则 → 重定义错误。类型变量约束不参与判断
- 构造函数:同一类中不同参数的构造函数 → 重载
- 主构造函数与
init:不同参数 → 重载
- 不同作用域(均可见)中的同名函数 → 重载
- 子类与父类同名函数不同参数类型 → 重载
7.2 不能重载的情况
7.3 重载解析
- 内层作用域优先:嵌套作用域中最内层匹配的函数优先
- 最具体匹配:同一作用域层级中,参数类型最具体(最窄)的函数被选择
- 父/子类视为同一作用域进行解析 — 更具体的匹配优先
- 若无唯一最佳匹配 → 错误
8. 运算符重载
8.1 语法
public operator func +(right: Point): Point { ... }
public operator func -(): Point { ... } // 一元运算符
- 在
func 前使用 operator 修饰符
- 一元运算符:无参数(操作
this)
- 二元运算符:一个参数(右操作数;左操作数为
this)
- 只能在 class、interface、struct、enum、extend 中定义
- 不能 为
static,不能 为泛型
- 不改变原有优先级或结合性
8.2 可重载的运算符
()、[]、!、-(一元)、**、*、/、%、+、-(二元)、<<、>>、<、<=、>、>=、==、!=、&、^、|
8.3 索引运算符 []
- 取值形式:
operator func [](args...): ReturnType — 仅非命名参数
- 赋值形式:
operator func [](args..., value!: T): Unit — 须有唯一命名参数 value!,无默认值,返回 Unit
- 可仅重载取值或赋值中的一个
8.4 函数调用运算符 ()
operator func ()(params): ReturnType — 任意参数/返回类型
- 不能通过
this() 或 super() 调用(它们始终引用构造函数)
8.5 复合赋值
- 重载二元运算符(关系运算除外)时自动启用对应复合赋值版本(
+=、-= 等),前提是返回类型与左操作数类型匹配或为其子类型
8.6 限制
- 不能创建自定义运算符(仅限列出的运算符)
- 不能重新重载类型已原生支持的运算符(如
Int64 的 +)
9. 函数调用语法糖
9.1 尾随 Lambda
- 当最后一个参数为函数类型且实参为 Lambda 时,Lambda 可置于括号外部:
myIf(true) { 100 }
- 若函数仅有一个参数(Lambda),括号可完全省略:
f { i => i * i }
- 在尾随 Lambda 位置,
=> 可省略
9.2 管道运算符 |>
e1 |> e2 等价于 let v = e1; e2(v)
e2 须为函数类型表达式;e1 的类型须为 e2 参数类型的子类型
- 可链式使用:
arr |> inc |> sum
- 不能直接用于有非默认命名参数的函数(需用 Lambda 包装)
9.3 组合运算符 ~>
f ~> g 等价于 { x => g(f(x)) }
f 和 g 须为单参数函数
f 的返回类型须为 g 的参数类型的子类型
9.4 变长参数
- 当最后一个非命名参数为
Array<T> 时,调用者可直接传递 0 个或多个值(而非数组字面量):
func sum(arr: Array<Int64>) { ... }
sum(1, 2, 3) // 脱糖为 sum([1, 2, 3])
- 仅最后一个非命名参数可变长。命名参数不能使用此语法糖
- 适用于:全局函数、静态/实例成员函数、局部函数、构造函数、Lambda、函数调用运算符重载、索引运算符重载
- 不适用于:其他运算符重载、
~>、|>
- 解析优先级:非变长匹配优先;仅当无非变长匹配时才尝试变长