function

SKILL.md

仓颉语言函数 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 非命名参数调用

  • 按位置传递表达式:add(x, y)

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 函数作为一等值

  • 函数可赋给变量作为参数传递从函数返回
  • 函数名本身是该函数类型的表达式
  • 若函数名重载且有歧义,直接赋给无类型标注的变量会报错。显式类型标注可消除歧义:
    var plus: (Int64, Int64) -> Int64 = add  // 消除歧义
    

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 捕获规则

  1. 被捕获的变量须在闭包定义处可见
  2. 被捕获的变量须在闭包定义前已初始化
  3. 若捕获的变量为引用类型,其可变实例成员可被修改
  4. 捕获 var 的闭包不能逃逸:不能赋给变量、返回、作为参数传递或作为独立表达式 — 只能直接调用
  5. 传递性捕获:若函数 f 调用捕获了 var 变量的函数 g(该 varf 的局部变量),则 f 也被视为捕获了 var,不能逃逸
  6. 静态/全局 var 变量不算捕获 — 访问它们的函数仍为一等值

6. 嵌套函数

  • 在其他函数体内定义的函数
  • 作用域:仅在外围函数内可见
  • 可访问外围函数的变量和参数
  • 可在外围函数中调用作为值返回
  • 生命周期:每次外围函数调用时创建,外围函数结束时销毁(除非作为闭包返回/捕获)

7. 函数重载

7.1 重载规则

  • 同名、不同参数数量或类型 → 有效重载
  • 泛型函数:对齐类型参数名后,若非泛型部分不同 → 重载;否则 → 重定义错误。类型变量约束不参与判断
  • 构造函数:同一类中不同参数的构造函数 → 重载
  • 主构造函数与 init:不同参数 → 重载
  • 不同作用域(均可见)中的同名函数 → 重载
  • 子类与父类同名函数不同参数类型 → 重载

7.2 不能重载的情况

  • 同一类/接口/结构体中同名的静态函数与实例函数(即使参数不同也不行):
    class Parser {
        // 错误:不能同时定义同名的实例方法和静态方法
        // public func parse(): Result { ... }
        // public static func parse(source: String): Result { ... }
    }
    
  • 枚举构造函数与静态/实例成员函数
  • 函数类型的变量不能互相重载
  • 变量和函数不能同名

7.3 重载解析

  1. 内层作用域优先:嵌套作用域中最内层匹配的函数优先
  2. 最具体匹配:同一作用域层级中,参数类型最具体(最窄)的函数被选择
  3. 父/子类视为同一作用域进行解析 — 更具体的匹配优先
  4. 若无唯一最佳匹配 → 错误

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)) }
  • fg 须为单参数函数
  • f 的返回类型须为 g 的参数类型的子类型

9.4 变长参数

  • 最后一个非命名参数Array<T> 时,调用者可直接传递 0 个或多个值(而非数组字面量):
    func sum(arr: Array<Int64>) { ... }
    sum(1, 2, 3)  // 脱糖为 sum([1, 2, 3])
    
  • 最后一个非命名参数可变长。命名参数不能使用此语法糖
  • 适用于:全局函数、静态/实例成员函数、局部函数、构造函数、Lambda、函数调用运算符重载、索引运算符重载
  • 不适用于:其他运算符重载、~>|>
  • 解析优先级:非变长匹配优先;仅当无非变长匹配时才尝试变长
Weekly Installs
3
First Seen
3 days ago
Installed on
opencode2
gemini-cli2
claude-code2
github-copilot2
codex2
kimi-cli2