Tuple 模式

tuple pattern 用于匹配 Tuple 值,tuple pattern 定义为由圆括号括起来的多个 pattern,每个pattern 之间使用逗号分隔:(pattern_1, pattern_2, ⋯ pattern_k)。例如,(x, y, z) 是由三个binding pattern 组成的一个 tuple pattern,(1, 0, 0) 是由三个 constant pattern 组成的一个tuple pattern。tuple pattern 中的子 pattern 个数需要和 selector 的维度相同,并且如果子 pattern是 constant pattern 或 enum pattern 时,其类型要和 selector 对应维度的类型相同。

tuple 模式的语法定义为:

tuplePattern
: '(' pattern (',' pattern)+ ')';

使用 tuple 模式的例子如下:

let scoreTuple = ("Allen", 90)
var scoreResult: String = match (scoreTuple) {
case ("Bob", 90) => "Bob got 90"
case ("Allen", score) => "Allen got ${score}" // matched
case ("Allen", 100) | ("Bob", 100) => "Allen or Bob got 100"case (_, _
}

类型模式

使用类型模式可以很方便地实现 type check 和 type cast。类型模式的语法定义为:

typePattern
: (wildcardPattern | varBindingPattern) ':' type;

对于类型模式 varBindingPattern : type(或 wildcardPattern : type)。首先判断要匹配的值
的运行时类型是否是 : 右侧 type 定义的类型或它的子类,若类型匹配成功则将值的类型转换为 type 定义的类型,然后将新类型的值与 : 左侧的 varBindingPattern 进行绑定(对于 wildcardPattern : type,不存在绑定)。只有类型匹配,才算成功匹配,否则匹配失败,因此,varBindingPattern : type(或wildcardPattern : type)可以同时实现 type test 和 type cast。

使用类型模式匹配的例子如下:

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 {
var color: String = "green"
init(x: Int32, y: Int32, color: String) {
super(x, y)
this.color = color
}
}
let normalPt = Point(5,10)
let colorPt = ColoredPoint(8,24,"red")
var rectangleArea1: Int32 = match (normalPt) {
case _: Point => normalPt.x * normalPt.y // matchedcase _ => 0
}
var rectangleArea2: Int32 = match (colorPt) {
case cpt: Point => cpt.x * cpt.y // matchedcase _ => 0
}

enum 模式

enum 模式主要和 enum 类型配合使用

enum pattern 用于匹配 enum constructor,格式是 constructorName(无参构造器)或 construc-torName(pattern_1,
pattern_2, …, pattern_k)(有参构造器),圆括号内用逗号分隔的若干 pattern(可以是其它任何类型的 pattern,并允许嵌套)依次对每个参数进行匹配。

enum 模式的语法定义为:

enumPattern
: (userType '.')? identifier enumPatternParameters?;
enumPatternParameters
: '(' pattern (',' pattern)* ')'
;
使用 enum 模式匹配的例子如下:
enum TimeUnit {
| Year(Float32)
| Month(Float32, Float32)
| Day(Float32, Float32, Float32)
| Hour(Float32, Float32, Float32, Float32)
}
let oneYear = TimeUnit.Year(1.0)
var howManyHours: Float32 = match (oneYear) {
case Year(y) => y * Float32(365 * 24) // matched
case Month(y, m) => y * Float32(365 * 24) + m * Float32(30 * 24)
case Day(y, m, d) => y * Float32(365 * 24) + m * Float32(30 * 24) + d *Float32(24)
case Hour(y, m, d, h) => y * Float32(365 * 24) + m * Float32(30 * 24) + d *Float32(24) + h
}
let twoYear = TimeUnit.Year(2.0)
var howManyYears: Float32 = match (twoYear) {
case Year(y) | Month(y, m) => y // error: variable cannot be introduced in
↪ patterns connected by '|'
case Year(y) | Month(x, _) => y // error: variable cannot be introduced in
↪ patterns connected by '|'
case Year(y) | Month(y, _) => y // error: variable cannot be introduced in
↪ patterns connected by '|'
...
}

如果模式匹配所在的作用域中,某个 pattern 中的 identiffer 是 enum 构造器时,该 identiffer 总是会被当成 enum pattern 进行匹配,否则才会作为 binding pattern 匹配。

enum Foo {
A | B | C
}
func f() {
let x = Foo.A
match (x) {
case A => 0 // enum pattern
case B => 1 // enum pattern
case C => 2 // enum pattern
case D => 3 // binding pattern}
}

需要注意的是,对 enum 进行匹配时,要求 enum pattern 的类型和 selector 的类型相同,同时编译器会检查 enum 类型的每个 constructor(包括 constructor 的参数的值)是否被完全覆盖,如果未做到全覆盖,则编译器会报错。

enum TimeUnit {
| Year(Float32)
| Month(Float32, Float32)
| Day(Float32, Float32, Float32)
| Hour(Float32, Float32, Float32, Float32)
}
let oneYear = TimeUnit.Year(1.0)
var howManyHours: Float32 = match (oneYear) { // error: match must be exhaustivecase Year(y) => y * Float32(365 * 24)
case Month(y, m) => y * Float32(365 * 24) + m * Float32(30 * 24)
}

模式的分类

一般地,在类型匹配的前提下,当一个 pattern 有可能和它所要匹配的值不匹配时,称此 pattern 为refutable pattern;反之,当一个 pattern 总是可以和它所要匹配的值匹配时,称此 pattern 为 irrefutablepattern。对于上述介绍的各类 pattern,规定:

• constant pattern 总是 refutable pattern;
• wildcard pattern 总是 irrefutable pattern;
• binding pattern 总是 irrefutable pattern;
• tuple pattern 是 irrefutable pattern,当且仅当其包含的每个 pattern 都是 irrefutable pattern;
• type pattern 总是 refutable pattern;
• enum pattern 是 irrefutable pattern,当且仅当其对应的 enum 类型中只有一个带参 constructor,且enum pattern 中包含的其他 pattern(如果存在)都是 irrefutable pattern。

字符串、字节和 Rune 的匹配规则

在模式匹配的目标是静态类型为 Rune 的值时,Rune 字面量和单字符字符串字面量都可用于表示 Rune类型字面量的常量 pattern。

在模式匹配的目标是静态类型为 Byte 的值时,一个表示 ASCII 字符的字符串字面量可用于表示 Byte 类型字面量的常量 pattern。

Pattern Guards

为了对匹配出来的值做进一步的判断,仓颉支持使用 pattern guard。pattern guard 可以在 match
表达式中使用,也可以在 for-in 表达式中使用。本节主要介绍 pattern guard 在 match 表达式中的使用,关于其在 for in 表达式中的使用请参见for-in 表达式。

match 表达式中,为了提供更加强大和精确的匹配模式,支持 pattern guard,即在 pattern 与 => 之间加上 where boolExpression(boolExpression 是值为布尔类型的表达式)。匹配的过程中,只有当值与 pattern 匹配并且满足 where 之后的 boolExpression 时,整个 case 才算匹配成功,否则匹配失败。

pattern guard 的语法定义为:

patternGuard
: 'where' expression
;
使用 pattern guards 的例子如下:
let oneYear = Year(1.0)
var howManyHours: Float32 = match (oneYear) {
case Year(y) where y > 0.0f32 => y * Float32(365 * 24) // matched
case Year(y) where y <= 0.0f32 => 0.0
case Month(y, m) where y > 0.0f32 && m > 0.0f32 => y * Float32(365 * 24) + m *Float32(30 * 24)
case Day(y, m, d) where y > 0.0f32 && m > 0.0f32 && d > 0.0f32 => y *Float32(365 * 24) + m * Float32(30 * 24) + d * Float32(24)
case Hour(y, m, d, h) where y > 0.0f32 && m > 0.0f32 && d > 0.0f32 && h > 0.0f32↪ => y * Float32(365 * 24) + m * Float32(30 * 24) + d * Float32(24) + h
case _ => 0.0
}

循环表达式

仓颉编程语言支持三种循环表达式:for-in 表达式、while 表达式和 do-while 表达式。循环表达式的语法定义为:


loopExpression
: forInExpression
| whileExpression
| doWhileExpression;

for-in 表达式

一个完整的 for-in 表达式具有如下形式:

for (p in e where c) {
s
}
其中 pattern guard where c 是非必须的,因此更简易的 for-in 表达式具有如下形式:for (p in e) {
s
}

for-in 表达式的语法定义为:

forInExpression
: 'for' '(' patternsMaybeIrrefutable 'in' expression patternGuard? ')' block;
patternsMaybeIrrefutable
: wildcardPattern
| varBindingPattern
| tuplePattern
| enumPattern
;
patternGuard
: 'where' expression
;

上述语法定义中,关键字 for 之后只能是那些一定或可能为 irrefutable 的 pattern(见模式的分类)。在语义检查阶段,会检查 for 之后的 pattern 是否真的是 irrefutable,如果不是 irrefutable pattern,则编译
报错。另外,如果 for 之后的 pattern 中存在 binding pattern,相当于新声明了一个(或多个)let 变量,每个变量的作用域从它第一次出现的位置到循环体结束。

for-in 会先对 expression 求值,再调用其 iterator() 函数,获取一个类型为 Iterator 的值。程序通过调用 Iterator 的 next() 函数开始执行循环,我们可以使用 pattern 匹配迭代的元素,如果匹配成功(如果存在 patternGuard,也必须同时满足 patternGuard 的条件),则执行循环体 block,然后在开始处重新调用 next() 继续循环,当 next() 返回 None 时循环终止。

Iterable 和 Iterator 可在标准库中查阅。

main(): Int64 {
let intArray: Array<Int32> = [0, 1, 2, 3, 4]for (item in intArray) {
print(item) // output: 01234
}
let intRange = 0..5
for (number in intRange where number > 2) {
print(number) // output: 34
}
return 0
}

while 表达式

while 表达式的语法定义为:

whileExpression
: 'while' '(' ('let' deconstructPattern '<-')? expression ')' block;

其中 while 是关键字,while 之后是一个小括号,小括号内可以是一个表达式或者一个 let 声明的解构匹配,接着是一个块。

一个基础的 while 表达式举例:

main(): Int64 {
var hundred = 0
while (hundred < 100) { // until hundred = 100hundred++
}
return 0
}

while 表达式首先对 while 之后的表达式进行求值(要求表达式的类型为 Bool),如果表达式的值等于 true,则执行它之后的块,接着重新计算表达式的值并判断是否重新执行一次循环;如果表达式的值等于false,则终止循环。

对于包含 let 的 while 表达式,我们称之为 while-let 表达式。我们可以用 while-let 表达式来做一些简单的解构操作。

一个基础的 while-let 表达式举例:

main(): Int64 {
var x: Option<Int64> = Option<Int64>.Some(100)// while-let expression
while (let Some(v) <- x) {
print("x has value")
x = ...
}
return 0
}

while-let 表达式首先对 <- 之后的表达式进行求值(表达式的类型为可以是任意类型),如果表达式的值能匹配 let 之后的 pattern,则执行它之后的块,接着重新计算表达式的值然后再次匹配并判断是否重新执行一次循环;如果匹配失败,则终止当前的 while 循环。

let 之后的 pattern 支持常量模式、通配符模式、绑定模式、Tuple 模式、enum 模式。

do-while 表达式

do-while 表达式的语法定义为:

doWhileExpression
: 'do' block 'while' '(' expression ')';

TRY 表达式

与 while 表达式不同的是:while 表达式在第一次循环迭代时,如果表达式 expression 的值为 false,则循环体不会被执行;然而对于 do-while 表达式,第一次循环迭代时,先执行循环体 block,然后再根据表达式 expression 的值决定是否再次执行循环体,也就是说 do-while 表达式中的循环体会至少执行一次。例如:

main(): Int64 {
var hundred = 0
do {
hundred++
} while (hundred < 100)return 0
}

循环表达式总结

for-in、while 和 do-while 这三种循环表达式的表达能力是等价的,通常,在知道循环的次数或遍历一个序列中的所有元素时使用 for-in 表达式;在不知道循环的次数,但知道循环终止条件时使用 while 或do-while 表达式。

三种循环表达式的类型均为 Unit。

由于 break、continue 表达式必须有包围着它们的循环体,所以对于三种循环表达式,其循环条件中出现的 break 或 continue 均会绑定到其最近的外层循环;如外层不存在循环,则报错。例如

while (true) {
println("outer") // printed once
do {
println("inner") // printed once
} while (break) // stop the execution of the outer loopprintln("unreached") // not printed
}
Logo

新一代开源开发者平台 GitCode,通过集成代码托管服务、代码仓库以及可信赖的开源组件库,让开发者可以在云端进行代码托管和开发。旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐