引言
在仓颉语言中,枚举(Enum)和模式匹配(Pattern Matching)是两个非常强大的特性,它们为代码提供了类型安全和表达力。枚举允许我们定义一组相关的值,而模式匹配则让我们能够优雅地处理这些值的不同情况。
1. 枚举类型(Enum)
1.1 枚举的基本概念
枚举类型是仓颉语言中定义一组相关值的强大工具。它类似于函数式编程语言中的代数数据类型(Algebraic Data Types),提供了类型安全和表达力。
1.2 枚举的定义
枚举使用 enum
关键字定义,基本语法如下:
cangjie
enum EnumName {
| Constructor1
| Constructor2(ParamType)
| Constructor3(ParamType1, ParamType2)
}
1.2.1 基本枚举示例
cangjie
// 简单的颜色枚举
enum Color {
| Red | Green | Blue
}
// 带参数的RGB颜色枚举
enum RGBColor {
| Red
| Green
| Blue
| Red(UInt8)
| Green(UInt8)
| Blue(UInt8)
}
1.2.2 非穷尽枚举(Non-exhaustive Enum)
仓颉支持非穷尽枚举,使用 ...
构造器:
cangjie
enum T {
| Red | Green | Blue | ...
}
重要说明:
...
构造器最多只能有一个,且必须是最后一个- 非穷尽枚举在模式匹配时需要特殊处理
...
构造器不能被直接匹配
1.2.3 递归枚举
枚举支持递归定义,这对于定义树形结构非常有用:
cangjie
enum Expr {
| Num(Int64)
| Add(Expr, Expr)
| Sub(Expr, Expr)
}
// 使用递归枚举构建表达式
let expr = Add(Num(5), Sub(Num(10), Num(3)))
1.2.4 枚举中的成员
枚举可以包含成员函数、操作符函数和属性:
cangjie
enum RGBColor {
| Red | Green | Blue
public static func printType(): Unit {
print("RGBColor")
}
public func getColorName(): String {
match (this) {
case Red => "红色"
case Green => "绿色"
case Blue => "蓝色"
}
}
}
注意事项:
- 构造器、成员函数、成员属性之间不能重名
- 枚举只能定义在源文件的顶层作用域
1.3 枚举的使用
1.3.1 创建枚举实例
cangjie
enum RGBColor <: ToString {
| Red | Green | Blue(UInt8)
public func toString(): String {
match (this) {
case Red => "红色"
case Green => "绿色"
case Blue(v) => "蓝色 ${v}"
}
}
}
main() {
// 方式1:使用类型名.构造器
let r = RGBColor.Red
// 方式2:直接使用构造器(当无歧义时)
let g = Green
let b = Blue(100)
print("r = ${r}, g = ${g}, b = ${b}")
}
1.3.2 名称冲突处理
当枚举构造器与其他标识符冲突时,必须使用类型名:
cangjie
let Red = 1
func Green(g: UInt8) {
return g
}
enum RGBColor {
| Red | Green(UInt8) | Blue(UInt8)
}
main() {
let r1 = Red // 选择 'let Red'
let r2 = RGBColor.Red // 正确:使用枚举类型名
let g1 = Green(100) // 选择 'func Green'
let g2 = RGBColor.Green(100) // 正确:使用枚举类型名
let b = Blue(100) // 正确:可以唯一识别为枚举构造器
}
2. 模式匹配(Pattern Matching)
2.1 match 表达式概述
仓颉支持两种 match
表达式:
- 包含待匹配值的
match
表达式 - 不含待匹配值的
match
表达式
2.2 包含匹配值的 match 表达式
2.2.1 基本语法
cangjie
match (expression) {
case pattern1 => expression1
case pattern2 => expression2
case _ => defaultExpression
}
2.2.2 基本示例
cangjie
main() {
let x = 0
let result = match (x) {
case 1 => "x = 1"
case 0 => "x = 0" // 匹配成功
case _ => "x != 1 and x != 0"
}
print(result)
}
执行结果:
ini
x = 0
2.2.3 穷尽性要求
match
表达式要求所有匹配必须是穷尽的,这意味着待匹配表达式的所有可能取值都应该被考虑到:
cangjie
// 错误示例:非穷尽匹配
func nonExhaustive(x: Int64) {
match (x) {
// non-exhaustive patterns, _ is not coveredCangjie
case 0 => print("x = 0")
case 1 => print("x = 1")
case 2 => print("x = 2")
// 缺少其他情况的处理
}
}
正确的穷尽匹配:
cangjie
func exhaustive(x: Int64) {
match (x) {
case 0 => print("x = 0")
case 1 => print("x = 1")
case 2 => print("x = 2")
case _ => print("x is other value")
}
}
2.2.4 非穷尽枚举的匹配
对于非穷尽枚举,需要使用可匹配所有构造器的模式:
cangjie
enum T {
| Red | Green | Blue | ...
}
func foo(a: T) {
match (a) {
case Red => 0
case Green => 1
case Blue => 2
case _ => -1 // 使用通配符模式
}
}
func bar(a: T) {
match (a) {
case Red => 0
case k => -1 // 使用绑定模式
}
}
func baz(a: T) {
match (a) {
case Red => 0
case k: T => -1 // 使用带类型的绑定模式
}
}
2.2.5 模式守卫(Pattern Guard)
使用 where
关键字添加额外的匹配条件:
cangjie
enum RGBColor {
| Red(Int16) | Green(Int16) | Blue(Int16)
}
main() {
let c = RGBColor.Green(-100)
let result = match (c) {
case Red(r) where r < 0 => "Red = 0"
case Red(r) => "Red = ${r}"
case Green(g) where g < 0 => "Green = 0" // 匹配成功
case Green(g) => "Green = ${g}"
case Blue(b) where b < 0 => "Blue = 0"
case Blue(b) => "Blue = ${b}"
}
print(result)
}
执行结果:
ini
Green = 0
2.3 不含匹配值的 match 表达式
这种 match
表达式直接判断布尔表达式:
cangjie
main() {
let x = -1
let result = match {
case x > 0 => "x > 0"
case x < 0 => "x < 0" // 匹配成功
case _ => "x = 0"
}
print(result)
}
执行结果:
x < 0
2.4 match 表达式的类型
2.4.1 上下文类型明确时
cangjie
let x = 2
let s: String = match (x) {
case 0 => "x = 0"
case 1 => "x = 1"
case _ => "x != 0 and x != 1"
}
2.4.2 上下文类型不明确时
cangjie
let x = 2
let s = match (x) {
case 0 => "x = 0"
case 1 => "x = 1"
case _ => "x != 0 and x != 1"
}
// s 的类型自动推断为 String
3. 模式类型详解
3.1 常量模式(Constant Pattern)
常量模式可以是各种字面量:
cangjie
main() {
let score = 90
let level = match (score) {
case 0 | 10 | 20 | 30 | 40 | 50 => "D"
case 60 => "C"
case 70 | 80 => "B"
case 90 | 100 => "A" // 匹配成功
case _ => "Not a valid score"
}
println(level)
}
执行结果:
css
A
3.1.1 特殊常量模式
Rune 类型匹配:
cangjie
func translate(n: Rune) {
match (n) {
case "A" => 1
case "B" => 2
case "C" => 3
case _ => -1
}
}
main() {
println(translate(r"C"))
}
Byte 类型匹配:
cangjie
func translate(n: Byte) {
match (n) {
case "1" => 1
case "2" => 2
case "3" => 3
case _ => -1
}
}
main() {
println(translate(51)) // UInt32(r'3') == 51
}
3.2 通配符模式(Wildcard Pattern)
使用 _
表示,可以匹配任意值:
cangjie
func wildcardExample(x: Int64) {
match (x) {
case _ => println("x is any value")
}
}
3.3 绑定模式(Binding Pattern)
使用标识符绑定匹配的值:
cangjie
main() {
let x = -10
let y = match (x) {
case 0 => "zero"
case n => "x is not zero and x = ${n}" // 匹配成功
}
println(y)
}
执行结果:
ini
x is not zero and x = -10
重要限制:
- 使用
|
连接多个模式时不能使用绑定模式 - 绑定模式不能嵌套出现在其他模式中
- 绑定的变量是不可变的
3.4 Tuple 模式(Tuple Pattern)
用于匹配 tuple 值:
cangjie
main() {
let tv = ("Alice", 24)
let s = match (tv) {
case ("Bob", age) => "Bob is ${age} years old"
case ("Alice", age) => "Alice is ${age} years old" // 匹配成功
case (name, 100) => "${name} is 100 years old"
case (_, _) => "someone"
}
println(s)
}
执行结果:
csharp
Alice is 24 years old
限制:
- 同一个 tuple 模式中不允许引入多个名称相同的绑定模式
3.5 类型模式(Type Pattern)
用于判断运行时类型:
cangjie
open class Base {
var a: Int64
public init() {
a = 10
}
}
class Derived <: Base {
public init() {
a = 20
}
}
func testTypePattern() {
var d = Derived()
var r = match (d) {
case b: Base => b.a // 匹配成功
case _ => 0
}
println("r = ${r}")
}
main() {
testTypePattern()
}
执行结果:
ini
r = 20
3.6 Enum 模式(Enum Pattern)
用于匹配枚举值:
cangjie
enum TimeUnit {
| Year(UInt64)
| Month(UInt64)
}
main() {
let x = Year(2)
let s = match (x) {
case Year(n) => "x has ${n * 12} months" // 匹配成功
case TimeUnit.Month(n) => "x has ${n} months"
}
println(s)
}
执行结果:
x has 24 months
重要要求:
- 使用
match
表达式匹配枚举值时,要求覆盖所有构造器 - 可以通过
case _
来覆盖未明确处理的情况
3.7 模式的嵌套组合
模式可以嵌套组合使用:
cangjie
enum TimeUnit {
| Year(UInt64)
| Month(UInt64)
}
enum Command {
| SetTimeUnit(TimeUnit)
| GetTimeUnit
| Quit
}
main() {
let command = (SetTimeUnit(Year(2022)), SetTimeUnit(Year(2024)))
match (command) {
case (SetTimeUnit(Year(year)), _) => println("Set year ${year}")
case (_, SetTimeUnit(Month(month))) => println("Set month ${month}")
case _ => ()
}
}
执行结果:
sql
Set year 2022
4. 模式的 Refutability
4.1 概念定义
- Refutable 模式:有可能和待匹配值不匹配的模式
- Irrefutable 模式:总是可以和待匹配值匹配的模式
4.2 各种模式的 Refutability
4.2.1 常量模式
常量模式是 refutable
模式:
cangjie
func constPat(x: Int64) {
match (x) {
case 1 => "one"
case 2 => "two"
case _ => "_"
}
}
4.2.2 通配符模式
通配符模式是 irrefutable
模式:
cangjie
func wildcardPat(x: Int64) {
match (x) {
case _ => "_"
}
}
4.2.3 绑定模式
绑定模式是 irrefutable
模式:
cangjie
func varPat(x: Int64) {
match (x) {
case a => "x = ${a}"
}
}
4.2.4 Tuple 模式
Tuple 模式是 irrefutable
模式,当且仅当其包含的每个模式都是 irrefutable
模式:
cangjie
func tuplePat(x: (Int64, Int64)) {
match (x) {
case (1, 2) => "(1, 2)" // refutable
case (a, 2) => "(${a}, 2)" // refutable
case (a, b) => "(${a}, ${b})" // irrefutable
}
}
4.2.5 类型模式
类型模式是 refutable
模式:
cangjie
interface I {}
open class Base <: I {}
class Derived <: Base {}
func typePat(x: I) {
match (x) {
case a: Derived => "Derived"
case b: Base => "Base"
case _ => "Other"
}
}
4.2.6 Enum 模式
Enum 模式是 irrefutable
模式,当且仅当它对应的枚举类型中只有一个有参构造器,且 enum 模式中包含的其他模式也是 irrefutable
模式:
cangjie
enum E1 {
A(Int64)
}
enum E2 {
B(Int64) | C(Int64)
}
func enumPat1(x: E1) {
match (x) {
case A(1) => "A(1)" // refutable
case A(a) => "A(${a})" // irrefutable
}
}
func enumPat2(x: E2) {
match (x) {
case B(b) => "B(${b})" // refutable
case C(c) => "C(${c})" // refutable
}
}
5. Option 类型
5.1 Option 类型概述
Option
类型是仓颉语言中处理"可能有值,也可能没有值"情况的标准方式。
5.2 Option 类型定义
cangjie
enum Option<T> {
| Some(T)
| None
}
5.3 简写语法
使用 ?
前缀:
cangjie
let a: Option<Int64> = Some(100)
let b: ?Int64 = Some(100) // 等价于 Option<Int64>
let c: Option<String> = Some("Hello")
let d: ?String = None
5.4 自动封装
当明确需要 Option<T>
类型时,可以直接传入 T
类型的值:
cangjie
let a: Option<Int64> = 100 // 自动封装为 Some(100)
let b: ?Int64 = 100 // 自动封装为 Some(100)
let c: Option<String> = "100" // 自动封装为 Some("100")
5.5 None 的使用
cangjie
let a = None<Int64> // a: Option<Int64>
let b = None<Bool> // b: Option<Bool>
6. 模式在其他地方的使用
6.1 变量定义中的模式
cangjie
// Enum 模式
enum RedColor {
Red(Int64)
}
main() {
// 通配符模式
let _ = 100
// 绑定模式
let x = 100
// Tuple 模式
let (x1, y) = (100, 200)
let Red(red) = Red(0)
println("x1 = ${x1}, y = ${y}, red = ${red}")
}
6.2 for in 表达式中的模式
cangjie
// Enum 模式
enum RedColor {
Red(Int64)
}
main() {
// 通配符模式
for (_ in 1..5) {
println("0")
}
// 绑定模式
for (i in 1..5) {
println(i)
}
// Tuple 模式
for ((i, j) in [(1, 2), (3, 4), (5, 6)]) {
println("Sum = ${i + j}")
}
for (Red(r) in [Red(10), Red(20), Red(30)]) {
println("r = ${r}")
}
}
重要限制:
- 只有
irrefutable
的模式才能在变量定义和for in
表达式中使用 - 允许的模式包括:通配符模式、绑定模式、
irrefutable
tuple 模式和irrefutable
enum 模式
7. 实际应用示例
7.1 状态机实现
cangjie
enum State {
| Idle
| Loading
| Success(String)
| Error(String)
}
func processState(state: State) {
match (state) {
case Idle => println("系统空闲")
case Loading => println("正在加载...")
case Success(data) => println("成功: ${data}")
case Error(msg) => println("错误: ${msg}")
}
}
main() {
let states = [Idle, Loading, Success("数据加载完成"), State.Error("网络错误")]
for (state in states) {
processState(state)
}
}
7.2 表达式求值器
cangjie
enum Expr {
| Num(Int64)
| Add(Expr, Expr)
| Sub(Expr, Expr)
| Mul(Expr, Expr)
| Div(Expr, Expr)
}
func evaluate(expr: Expr): Int64 {
match (expr) {
case Num(n) => n
case Add(left, right) => evaluate(left) + evaluate(right)
case Sub(left, right) => evaluate(left) - evaluate(right)
case Mul(left, right) => evaluate(left) * evaluate(right)
case Div(left, right) =>
let rightVal = evaluate(right)
if (rightVal == 0) {
throw Exception("除零错误")
}
evaluate(left) / rightVal
}
}
main() {
let expr = Add(
Mul(Num(5), Num(3)),
Sub(Num(10), Num(2))
)
let result = evaluate(expr)
println("表达式结果: ${result}")
}
7.3 错误处理
cangjie
enum Result<T, E> {
| Ok(T)
| Err(E)
}
func divide(a: Int64, b: Int64): Result<Int64, String> {
if (b == 0) {
return Err("除零错误")
}
return Ok(a / b)
}
func handleDivision(a: Int64, b: Int64) {
let result = divide(a, b)
match (result) {
case Ok(value) => println("除法结果: ${value}")
case Err(message) => println("错误: ${message}")
}
}
main() {
handleDivision(10, 2)
handleDivision(10, 0)
}
8. 最佳实践和注意事项
8.1 枚举设计原则
- 语义清晰:枚举构造器的名称应该清晰表达其含义
- 参数合理:为构造器添加必要的参数,避免过度设计
- 穷尽性:确保模式匹配覆盖所有可能的情况
8.2 模式匹配最佳实践
- 使用通配符 :在最后一个 case 中使用
_
确保穷尽性 - 模式顺序:将具体的模式放在前面,通用的模式放在后面
- 避免重复 :使用
|
连接多个相似的模式
8.3 性能考虑
- 编译时优化:编译器会优化模式匹配,通常比 if-else 链更高效
- 穷尽性检查:编译时检查确保不会遗漏任何情况
8.4 常见陷阱
- 非穷尽匹配:忘记处理某些情况会导致编译错误
- 绑定模式冲突 :在
|
连接的模式中使用绑定模式 - 类型不匹配:模式类型与待匹配值类型不兼容