仓颉编程语言的match表达式

发布于:2025-08-10 ⋅ 阅读:(18) ⋅ 点赞:(0)

仓颉编程语言的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 = 85
    let 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 = 1
    var y: Int32 = 2
    init(x: Int32, y: Int32) {
        this.x = x
        this.y = y
    }
}
class ColoredPoint <: Point {  // 继承自Point
    var color: String = "green"
    init(x: Int32, y: Int32, color: String) {
        super(x, y)  // 调用父类构造函数
        this.color = color
    }
}

main() {
    // 创建两个实例:父类对象和子类对象
    let normalPt = Point(5, 10)  // 类型是Point
    let 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类型并绑定到cpt
        case _ => 0
    }

    println("area1 = ${rectangleArea1}") // 50
    println("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 = 55
    match {
        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,高4

    let 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         => 200
        case NotFound   => 404
        case ServerError => 500
        case _          => -1    // 必须兜底,否则编译报错
    }
}

main() {
    println(codeOf(HttpStatus.NotFound))  // → 404
}


网站公告

今日签到

点亮在社区的每一天
去签到