仓颉开发之——枚举和模式匹配(8)

一 概述

  • 枚举类型
  • Option类型
  • 模式概述
  • 模式的Refutability
  • 常见表达式

二 枚举类型

2.1 枚举定义

1
2
3
4
5
6
7
8
enum RGBColor {
| Red | Green | Blue
| Red(UInt8) | Green(UInt8) | Blue(UInt8)

public static func printType() {
print("RGBColor")
}
}

说明:

  • 使用enum定义枚举类型
  • 在 enum 体中还可以定义一系列成员函数、操作符函数和成员属性

2.2 枚举的使用

1
2
3
4
5
6
7
8
9
enum RGBColor {
| Red | Green | Blue(UInt8)
}

main() {
let r = RGBColor.Red
let g = Green
let b = Blue(100)
}

说明:

  • 创建枚举实例,调用枚举实例的属性或方法
  • 注意:构造枚举实例时是否需要传参

三 Option类型(enum的一种特例)

3.1 Option介绍

  • enum的一种特例
  • 它包含两个构造器:Some 和 None
  • Some 会携带一个参数,表示有值,None 不带参数,表示无值
  • Some 构造器的参数类型就是类型形参 T,当 T 被实例化为不同的类型时,会得到不同的 Option 类型,例如:Option<Int64>、Option<String>等
  • Option 类型还有一种简单的写法:在类型名前加 ? 例如,?Int64 等价于 Option<Int64>,?String 等价于 Option<String> 等等

3.2 Option定义

1
2
3
4
let a: Option<Int64> = Some(100)
let b: ?Int64 = Some(100)
let c: Option<String> = Some("Hello")
let d: ?String = None

3.3 Option封装

1
2
3
let a: Option<Int64> = 100
let b: ?Int64 = 100
let c: Option<String> = "100"

说明:根据类型推断,100会被封装成Some(100)

3.3 None

1
2
let a = None<Int64> // a: Option<Int64>
let b = None<Bool> // b: Option<Bool>

四 模式概述

介绍仓颉支持的模式,包括:常量模式、通配符模式、绑定模式、tuple 模式、类型模式和 enum 模式

4.1 常量模式

1
2
3
4
5
6
7
8
9
10
11
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" // Matched.
case _ => "Not a valid score"
}
println(level)
}

说明:

  • 常量模式可以是整数字面量、浮点数字面量、字符字面量、布尔字面量、字符串字面量(不支持字符串插值)、Unit 字面量
  • 要求常量模式表示的值的类型与待匹配值的类型相同

4.2 通配符模式(即下划线 _ )

1
case _ => -1

说明:

  • 下划线 _ 表示,可以匹配任意值
  • 通配符模式通常作为最后一个 case 中的模式,用来匹配其他 case 未覆盖到的情况

4.3 绑定模式

1
2
3
4
5
6
7
8
main() {
let x = -10
let y = match (x) {
case 0 => "zero"
case n => "x is not zero and x = ${n}" // Matched.
}
println(y)
}

说明:

  • 绑定模式会将匹配到的值与 id 进行绑定,在 => 之后可以通过 id 访问其绑定的值
  • 使用 | 连接多个模式时不能使用绑定模式,也不可嵌套出现在其它模式中,否则会报错

4.4 tuple 模式

1
2
3
4
5
6
7
8
9
10
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" // Matched
case (name, 100) => "${name} is 100 years old"
case (_, _) => "someone"
}
println(s)
}

说明:

  • Tuple 模式用于 tuple 值的匹配
  • 同一个 tuple 模式中不允许引入多个名字相同的绑定模式

4.5 类型模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
open class Base {
var a: Int64
public init() {
a = 10
}
}

class Derived <: Base {
public init() {
a = 20
}
}
main() {
var d = Derived()
var r = match (d) {
case b: Base => b.a // Matched.
case _ => 0
}
println("r = ${r}")
}

执行结果

1
r = 20

说明:

  • 类型模式用于判断一个值的运行时类型是否是某个类型的子类型
  • Base 和 Derived,并且 Derived 是 Base 的子类,Base 的无参构造函数中将 a 的值设置为 10,Derived 的无参构造函数中将 a 的值设置为 20

4.6 模式的嵌套组合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
enum TimeUnit {
| Year(UInt64)
| Month(UInt64)
}

enum Command {
| SetTimeUnit(TimeUnit)
| GetTimeUnit
| Quit
}

main() {
let command = SetTimeUnit(Year(2022))
match (command) {
case SetTimeUnit(Year(year)) => println("Set year ${year}")
case SetTimeUnit(Month(month)) => println("Set month ${month}")
case _ => ()
}
}

打印结果

1
Set year 2022

说明:

  • Tuple 模式和 enum 模式可以嵌套任意模式
  • 图示为两种枚举类型组合

五 模式的Refutability

5.1 说明

  • 模式可以分为两类:refutable 模式和 irrefutable 模式
  • 在类型匹配的前提下,当一个模式有可能和待匹配值不匹配时,称此模式为 refutable 模式
  • 反之,当一个模式总是可以和待匹配值匹配时,称此模式为 irrefutable 模式。

5.2 refutable 模式(不匹配)

1-常量模式(下例中第一个 case 中的 1 和第二个 case 中的 2 都有可能和 x 的值不相等)

1
2
3
4
5
6
7
func constPat(x: Int64) {
match (x) {
case 1 => "one"
case 2 => "two"
case _ => "_"
}
}

2-类型模式

1
2
3
4
5
6
7
8
9
10
11
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"
}
}

5.3 irrefutable 模式(匹配)

1-通配符模式(无论 x 的值是多少,_ 总能和其匹配)

1
2
3
4
5
func wildcardPat(x: Int64) {
match (x) {
case _ => "_"
}
}

2-绑定模式(无论 x 的值是多少,绑定模式 a 总能和其匹配)

1
2
3
4
5
func varPat(x: Int64) {
match (x) {
case a => "x = ${a}"
}
}

3-Tuple 模式(当且仅当其包含的每个模式都是 irrefutable 模式)

1
2
3
4
5
6
7
func tuplePat(x: (Int64, Int64)) {
match (x) {
case (1, 2) => "(1, 2)"
case (a, 2) => "(${a}, 2)"
case (a, b) => "(${a}, ${b})"
}
}

4-enum 模式(当且仅当它对应的 enum 类型中只有一个有参构造器,且 enum 模式中包含的其他模式也是 irrefutable 模式)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
enum E1 {
A(Int64)
}

enum E2 {
B(Int64) | C(Int64)
}

func enumPat1(x: E1) {
match (x) {
case A(1) => "A(1)"
case A(a) => "A(${a})"
}
}

func enumPat2(x: E2) {
match (x) {
case B(b) => "B(${b})"
case C(c) => "C(${c})"
}
}

六 常见表达式

6.1 match表达式

1
2
3
4
5
6
7
8
9
10
11
main() {
let x = 0
match (x) {
case 1 => let r1 = "x = 1"
print(r1)
case 0 => let r2 = "x = 0" // Matched.
print(r2)
case _ => let r3 = "x != 1 and x != 0"
print(r3)
}
}

6.2 if-let表达式

1
2
3
4
5
6
7
8
9
main() {
let result = Option<Int64>.Some(2023)

if (let Some(value) <- result) {
println("操作成功,返回值为:${value}")
} else {
println("操作失败")
}
}

说明:

  • 对条件中 <- 右侧的表达式进行求值,如果此值能匹配 <- 左侧的模式,则执行 if 分支,否则执行 else 分支

6.3 while-let表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import std.random.*

// 此函数模拟在通信中接收数据,获取数据可能失败
func recv(): Option<UInt8> {
let number = Random().nextUInt8()
if (number < 128) {
return Some(number)
}
return None
}

main() {
// 模拟循环接收通信数据,如果失败就结束循环
while (let Some(data) <- recv()) {
println(data)
}
println("receive failed")
}

说明:

  • while-let 表达式首先对条件中 <- 右侧的表达式进行求值
  • 如果此值能匹配 <- 左侧的模式,则执行循环体,然后重复执行此过程
  • 如果模式匹配失败,则结束循环,继续执行 while-let 表达式之后的代码

七 思维导图

八 参考

  • 仓颉官方文档—枚举
  • 仓颉编程语言入门教程