仓颉编程语言的match表达式
仓颉编程语言的match表达式
【官方文档 文档-仓颉编程语言官网 】
仓颉编程语言中支持使用模式匹配表达式(match 表达式)实现模式匹配(pattern matching),允许开发者使用更精简的代码描述复杂的分支控制逻辑。
仓颉支持两种 match 表达式,第一种是包含待匹配值的 match 表达式,第二种是不含待匹配值的 match 表达式:带值的 match 用“模式”去比对具体数据,不带值的 match 用“布尔条件”做分支。
按顺序匹配,一旦命中就立即执行对应代码,并结束整个 match;不能处理就继续往下问,直到问完为止。
match表达式可以看作是多个 if-else if-else 的简化写法。但 match 能显著提升处理多场景逻辑的效率,尤其是在枚举、复杂数据结构的场景中,优势远胜于传统的 if-else。
main() {let score = 82// if-else 写法if (score >= 90) {println("优秀")} else if (score >= 80) {println("良好")} else if (score >= 60) {println("及格")} else {println("不及格")}// match 写法(更简洁,分支更清晰)match {case score >= 90 => println("优秀")case score >= 80 => println("良好")case score >= 60 => println("及格")case _ => println("不及格")}
}
从此例看, match 确实是 if-else 的简化,减少了重复的 else if 关键字,分支逻辑更直观。但是,match 具有 if-else 无法替代能力。if-else 处理枚举(enum)、元组(tuple)、结构(struct)等复杂类型时,需要手动判断类型、提取成员,代码繁琐且易出错;而 match 的模式匹配能直接解构数据。
仓颉编程语言提供了丰富的模式种类:
常量模式
使用字面量(如整数、字节、浮点、字符、布尔、字符串、Unit),要求 字面量的类型必须和待匹配值(文档中的 selector)类型一致 且 值相等 才算匹配成功。
通配符模式
可以使用下划线 _ 通配符(wildcard),它可以匹配任意值,但不会保存匹配到的值(相当于 “忽略这个值”)。 通常放在最后兜底——作为 match 表达式的最后一个 pattern 来匹配其它 case 未覆盖到的情况,确保穷尽。
绑定模式
绑定模式( binding pattern)和通配符一样能匹配任意值,但需要将变量名写在模式里,并且会将匹配到的值绑定到一个变量,供 => 后面的代码使用。绑定模式中定义的变量是不可变的。
元组模式(tuple patterns)
元组模式用于匹配元组(Tuple)类型的值(由多个值组合成的整体,如 (a, b)、(name, age, score))。它通过 “按位置匹配元组的每个元素”,实现对元组的解构或条件判断。其中子模式的数量必须和待匹配元组的 “维度”(元素个数)完全一致。
类型模式(type patterns)
类型模式(type pattern)用于判断值的运行时类型,并自动完成类型转换(type cast)——自动向下转型,适合处理多态场景(如父类与子类的对象)。
类型模式有两种形式:
_ : 类型 // 只检查不绑定,通配符模式 _
绑定名 : 类型 // 绑定变量并转型
它们的区别是后者会发生变量绑定,而前者不会。
enum 模式
enum 模式主要和 enum (枚举)类型配合使用。
结构与枚举构造器一致:无参构造器对应 case 构造器名,带参构造器对应 case 构造器名(子模式...);
匹配条件严格:必须构造器名相同,且所有参数与子模式匹配;
穷尽性要求:match 表达式必须覆盖所有可能的构造器,否则编译器报错。
模式守卫(Pattern Guards)
即在 pattern 与 => 之间加上 where boolExpression(其中boolExpression是值为布尔类型的表达式)。匹配的过程中,只有当值与 pattern 匹配并且满足 where 之后的 boolExpression 时, case 才算匹配成功,否则匹配失败。
下面给出一组示例。
例 1:简单数字分类
main() {// ① 带匹配值let score = 85let level = match (score / 10) {case 10 | 9 => "优秀"case 8 => "良好" // 命中,下面三行可能引发警告信息【注】case 7 => "中等" case 6 => "及格"case _ => "不及格"}println("带匹配值: ${level}") // 输出:带匹配值: 良好// ② 不带匹配值match {case score >= 90 => println("不带匹配值: 优秀")case score >= 80 => println("不带匹配值: 良好") // 命中,输出:不带匹配值: 良好case score >= 70 => println("不带匹配值: 中等")case score >= 60 => println("不带匹配值: 及格")case _ => println("不带匹配值: 不及格")}
}
其中:10 | 9 不是“位运算”,而是“或模式”——只要匹配 10 或 9 就走这条分支。
【注】:当匹配值是编译时可确定的常量时,它会检查哪些分支永远不会被执行,并提示这些分支冗余:
warning: unreachable pattern
……
# note: this warning can be suppressed by setting the compiler option `-Woff unused`
(意思是
警告:不可访问的模式
……
注意:这个警告可以通过设置编译器选项“-Woff unused”来消除。)
可以不用管它。解释:
警告的本质是编译器的 “智能提醒”:当匹配值是编译时可确定的常量时,它会检查哪些分支永远不会被执行,并提示这些分支冗余。如果你的代码确实需要保留这些分支(为了扩展性),让匹配值 “动态化”(如通过函数、输入获取)即可消除警告。
如将
let score = 85
改为:
func getScore() : Int8 {
85 // 即使返回固定值,函数调用让编译器无法提前确定
}
或
print("请输入:")
let str: String = readln()
let score = Int64.parse(str) //需要 import std.convert.*
用这两种方式之一消除警告。
例 2:结合模式守卫(Pattern Guards)使用绑定的变量
main() {let score = 88// 结合条件(where)使用绑定的变量match (score) {// 匹配任意值绑定到s,再用where判断范围case s where s >= 90 => println("优秀,分数:${s}")case s where s >= 60 => println("及格,分数:${s}") // 匹配88,输出“及格,分数:88”case s => println("不及格,分数:${s}")}
}
例 3:元组模式示例
main() {// 定义一个元组:(姓名, 分数)let scoreTuple = ("Allen", 90)// 用元组模式匹配这个元组var scoreResult: String = match (scoreTuple) { case ("Bob", 90) => "Bob got 90" // 子模式都是常量:匹配("Bob", 90)case ("Allen", score) => "Allen got ${score}" // 命中:第一个元素是"Allen"(常量模式),第二个元素绑定到变量score(绑定模式)case ("Allen", 100) | ("Bob", 100) => "Allen or Bob got 100" // 多模式组合:匹配("Allen",100)或("Bob",100)case (_, _) => "" // 通配符模式:匹配所有其他元组(兜底)}println(scoreResult) // → Allen got 90
}
说明:
待匹配元组是 ("Allen", 90),维度为 2(2 个元素),因此所有元组模式必须包含 2 个子模式(否则不匹配)。其中子模式的数量必须和待匹配元组的 “维度”(元素个数)完全一致。
第一个 case ("Bob", 90):第一个子模式 "Bob" 与元组第一个元素 "Allen" 不匹配,跳过。
第二个 case ("Allen", score):第一个子模式 "Allen" 匹配成功,第二个子模式 score(绑定模式)将元组的 90 绑定到 score 变量,整体匹配成功,执行对应代码。
例 4:类型模式(type pattern)示例
// 定义父类Point和子类ColoredPoint(多态场景)
open class Point { var x: Int32 = 1var y: Int32 = 2init(x: Int32, y: Int32) {this.x = xthis.y = y}
}
class ColoredPoint <: Point { // 继承自Pointvar color: String = "green"init(x: Int32, y: Int32, color: String) {super(x, y) // 调用父类构造函数this.color = color}
}main() {// 创建两个实例:父类对象和子类对象let normalPt = Point(5, 10) // 类型是Pointlet colorPt = ColoredPoint(8, 24, "red") // 类型是ColoredPoint(也是Point的子类)// 用类型模式匹配normalPt(Point类型)var rectangleArea1: Int32 = match (normalPt) {case _: Point => normalPt.x * normalPt.y // 命中:normalPt是Point类型,类型匹配,计算面积case _ => 0}// 用类型模式匹配colorPt(ColoredPoint类型,是Point的子类)var rectangleArea2: Int32 = match (colorPt) {case cpt: Point => cpt.x * cpt.y // 命中:colorPt是Point的子类,类型匹配,转换为Point类型并绑定到cptcase _ => 0}println("area1 = ${rectangleArea1}") // 50println("area2 = ${rectangleArea2}") // 192
}
说明:
对于 normalPt(类型为 Point):
case _: Point 中,_: Point 检查 normalPt 的类型是否是 Point(或其子类),显然是,因此匹配成功,执行面积计算。
对于 colorPt(类型为 ColoredPoint,是 Point 的子类):
case cpt: Point 中,cpt: Point 检查 colorPt 的类型是否兼容 Point(子类兼容父类),类型匹配;然后将 colorPt 转换为 Point 类型并绑定到 cpt,通过 cpt.x 和 cpt.y 访问属性(转换后仍可访问父类成员)。
例 5:枚举示例
enum TrafficLight { | Red | Yellow | Green }main() {let light = TrafficLight.Yellow// ① 带匹配值(穷尽早退)let msg = match (light) {case Red => "停车"case Yellow => "注意" // 命中case Green => "通行"}println("交通灯: ${msg}") // → 交通灯:注意// ② 不带匹配值,用变量做条件let speed = 55match {case speed > 120 => println("超速警告")case speed >= 80 => println("正常行驶")case speed > 0 => println("低速行驶") // 命中case _ => println("车辆静止")}
}
说明:TrafficLight 只有 3 个构造器 → 3 条 case 已全覆盖Red、Yellow、Green,不需要 _。
例 6:带多参数的枚举构造器
// 定义一个表示“形状”的枚举,包含带多参数的构造器
enum Shape {| Circle(Int) // 圆:半径(Int)| Rectangle(Int, Int) // 矩形:宽、高(Int, Int)| Square(Int) // 正方形:边长(Int)
}main() {let shape = Shape.Rectangle(3, 4) // 矩形实例:宽3,高4let area = match (shape) {case Circle(r) => 3 * r * r // 圆面积:3×r²(简化计算)case Rectangle(w, h) => w * h // 命中:构造器名Rectangle,参数3与w匹配,4与h匹配case Square(s) => s * s // 正方形面积:边长²}println("面积:${area}") // 输出:面积:12(3×4)
}
说明:
shape 是 Rectangle(3,4) 实例,case Rectangle(w, h) 中:
构造器名 Rectangle 与实例一致;
第一个参数 3 与绑定模式 w 匹配(w=3),第二个参数 4 与 h 匹配(h=4),因此匹配成功,计算面积 3×4=12。
例 7:非穷尽枚举必须用 _ 兜底,否则直接编译不过
enum HttpStatus {| Ok | NotFound | ServerError | ...
}func codeOf(s: HttpStatus): Int64 {match (s) {case Ok => 200case NotFound => 404case ServerError => 500case _ => -1 // 必须兜底,否则编译报错}
}main() {println(codeOf(HttpStatus.NotFound)) // → 404
}