仓颉语言函数 Skill
- 函数定义
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.1 基本调用语法
f(arg1, arg2, ..., argn)
- 每个实参的类型须为对应形参类型的子类型
2.2 非命名参数调用
- 按位置传递表达式:add(x, y)
2.3 命名参数调用
-
使用 paramName: value 语法:add(b: y, a: x)
-
命名实参的顺序可以与定义顺序不同
2.4 默认值
-
若调用时未提供带默认值的命名参数,则使用默认值
-
若提供了新值,则覆盖默认值
- 函数类型 / 一等公民
3.1 函数类型语法
-
(ParamType1, ParamType2, ...) -> ReturnType
-
-> 右结合
-
示例:
-
() -> Unit — 无参,返回 Unit
-
(Int64, Int64) -> Int64 — 两个 Int64 参数,返回 Int64
-
() -> (Int64, Int64) -> Int64 — 返回一个函数
3.2 类型参数名
-
函数类型可选包含参数名:(name: String, price: Int64) -> Unit
-
须全部具名或全部不具名,不可混合
3.3 函数作为一等值
-
函数可赋给变量、作为参数传递、从函数返回
-
函数名本身是该函数类型的表达式
-
若函数名重载且有歧义,直接赋给无类型标注的变量会报错。显式类型标注可消除歧义: var plus: (Int64, Int64) -> Int64 = add // 消除歧义
- 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.1 定义
闭包 = 函数/Lambda + 从其定义(词法)作用域捕获的变量
5.2 什么算作变量捕获
-
访问在函数/Lambda 外部定义的局部变量
-
在默认参数值中访问函数外部的变量
-
在类/结构体中,非成员函数/Lambda 访问实例成员或 this
5.3 什么不算作捕获
-
访问自身的局部变量或参数
-
访问全局变量或静态成员变量
-
实例成员函数中通过隐式 this 访问实例成员
5.4 捕获规则
-
被捕获的变量须在闭包定义处可见
-
被捕获的变量须在闭包定义前已初始化
-
若捕获的变量为引用类型,其可变实例成员可被修改
-
捕获 var 的闭包不能逃逸:不能赋给变量、返回、作为参数传递或作为独立表达式 — 只能直接调用
-
传递性捕获:若函数 f 调用捕获了 var 变量的函数 g (该 var 非 f 的局部变量),则 f 也被视为捕获了 var ,不能逃逸
-
静态/全局 var 变量不算捕获 — 访问它们的函数仍为一等值
- 嵌套函数
-
在其他函数体内定义的函数
-
作用域:仅在外围函数内可见
-
可访问外围函数的变量和参数
-
可在外围函数中调用或作为值返回
-
生命周期:每次外围函数调用时创建,外围函数结束时销毁(除非作为闭包返回/捕获)
- 函数重载
7.1 重载规则
-
同名、不同参数数量或类型 → 有效重载
-
泛型函数:对齐类型参数名后,若非泛型部分不同 → 重载;否则 → 重定义错误。类型变量约束不参与判断
-
构造函数:同一类中不同参数的构造函数 → 重载
-
主构造函数与 init :不同参数 → 重载
-
不同作用域(均可见)中的同名函数 → 重载
-
子类与父类同名函数不同参数类型 → 重载
7.2 不能重载的情况
-
同一类/接口/结构体中同名的静态函数与实例函数(即使参数不同也不行): class Parser { // 错误:不能同时定义同名的实例方法和静态方法 // public func parse(): Result { ... } // public static func parse(source: String): Result { ... } }
-
枚举构造函数与静态/实例成员函数
-
函数类型的变量不能互相重载
-
变量和函数不能同名
7.3 重载解析
-
内层作用域优先:嵌套作用域中最内层匹配的函数优先
-
最具体匹配:同一作用域层级中,参数类型最具体(最窄)的函数被选择
-
父/子类视为同一作用域进行解析 — 更具体的匹配优先
-
若无唯一最佳匹配 → 错误
- 运算符重载
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..., value!: T): Unit — 须有唯一命名参数 value! ,无默认值,返回 Unit
-
可仅重载取值或赋值中的一个
8.4 函数调用运算符 ()
-
operator func ()(params): ReturnType — 任意参数/返回类型
-
不能通过 this() 或 super() 调用(它们始终引用构造函数)
8.5 复合赋值
- 重载二元运算符(关系运算除外)时自动启用对应复合赋值版本(+= 、-= 等),前提是返回类型与左操作数类型匹配或为其子类型
8.6 限制
-
不能创建自定义运算符(仅限列出的运算符)
-
不能重新重载类型已原生支持的运算符(如 Int64 的 + )
- 函数调用语法糖
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、函数调用运算符重载、索引运算符重载
-
不适用于:其他运算符重载、~> 、|>
-
解析优先级:非变长匹配优先;仅当无非变长匹配时才尝试变长