三、do-while 表达式
do-while 表达式的基本形式为:
do {
循环体
} while (条件)
其中“条件”是布尔类型表达式,“循环体”是一个代码块。do-while 表达式将按如下规则执行:
执行“循环体”,转第 2 步。
计算“条件”表达式,如果值为 true 则转第 1 步,值为 false 转第 3 步。
结束循环,继续执行 do-while 表达式后面的代码。
例如,以下程序使用 do-while 表达式,基于蒙特卡洛算法,近似计算圆周率的值:
import std.random.*
main() {
let random = Random()
var totalPoints = 0
var hitPoints = 0
do {
// 在 ((0, 0), (1, 1)) 这个正方形中随机取点
let x = random.nextFloat64()
let y = random.nextFloat64()
// 判断是否落在正方形内接圆里
if ((x - 0.5) ** 2 + (y - 0.5) ** 2 < 0.25) {
hitPoints++
}
totalPoints++
} while (totalPoints < 1000000)
let pi = 4.0 * Float64(hitPoints) / Float64(totalPoints)
println("圆周率近似值为:${pi}")
}
运行以上程序,将输出:
圆周率近似值为:3.141872
说明
由于算法涉及随机数,所以每次运行程序输出的数值可能都不同,但都会约等于 3.14。
四、for-in 表达式
for-in 表达式可以遍历那些扩展了迭代器接口 Iterable 的类型实例。for-in 表达式的基本形式为:
for (迭代变量 in 序列) {
循环体
}
其中“循环体”是一个代码块。“迭代变量”是单个标识符或由多个标识符构成的元组,用于绑定每轮遍历中由迭代器指向的数据,可以作为“循环体”中的局部变量使用。“序列”是一个表达式,它只会被计算一次,遍历是针对此表达式的值进行的,其类型必须扩展了迭代器接口 Iterable。for-in 表达式将按如下规则执行:
计算“序列”表达式,将其值作为遍历对象,并初始化遍历对象的迭代器。
更新迭代器,如果迭代器终止,转第 4 步,否则转第 3 步。
将当前迭代器指向的数据与“迭代变量”绑定,并执行“循环体”,转第 2 步。
结束循环,继续执行 for-in 表达式后面的代码。
说明
仓颉内置的区间和数组等类型已经扩展了 Iterable 接口。
例如,以下程序使用 for-in 表达式,遍历中国地支字符构成的数组 noumenonArray,输出农历 2024 年各月的干支纪法:
main() {
let metaArray = [r'甲', r'乙', r'丙', r'丁', r'戊',
r'己', r'庚', r'辛', r'壬', r'癸']
let noumenonArray = [r'寅', r'卯', r'辰', r'巳', r'午', r'未',
r'申', r'酉', r'戌', r'亥', r'子', r'丑']
let year = 2024
// 年份对应的天干索引
let metaOfYear = ((year % 10) + 10 - 4) % 10
// 此年首月对应的天干索引
var index = (2 * metaOfYear + 3) % 10 - 1
println("农历 2024 年各月干支:")
for (noumenon in noumenonArray) {
print("${metaArray[index]}${noumenon} ")
index = (index + 1) % 10
}
}
运行以上程序,将输出:
农历 2024 年各月干支:
丙寅 丁卯 戊辰 己巳 庚午 辛未 壬申 癸酉 甲戌 乙亥 丙子 丁丑
1.遍历区间
for-in 表达式可以遍历区间类型实例,例如:
main() {
var sum = 0
for (i in 1..=100) {
sum += i
}
println(sum)
}
运行以上程序,将输出:
5050
2.遍历元组构成的序列
如果一个序列的元素是元组类型,则使用 for-in 表达式遍历时,“迭代变量”可以写成元组形式,以此实现对序列元素的解构,例如:
main() {
let array = [(1, 2), (3, 4), (5, 6)]
for ((x, y) in array) {
println("${x}, ${y}")
}
}
运行以上程序,将输出:
1, 2
3, 4
5, 6
3.迭代变量不可修改
在 for-in 表达式的循环体中,不能修改迭代变量,例如以下程序在编译时会报错:
main() {
for (i in 0..5) {
i = i * 10 // Error, cannot assign to value which is an initialized 'let' constant
println(i)
}
}
4.使用通配符 _ 代替迭代变量
在一些应用场景中,我们只需要循环执行某些操作,但并不使用迭代变量,这时您可以使用通配符 _ 代替迭代变量,例如:
main() {
var number = 2
for (_ in 0..5) {
number *= number
}
println(number)
}
运行以上程序,将输出:
4294967296
注意:在这种场景下,如果您使用普通的标识符定义迭代变量,编译会输出“unused variable”告警,使用通配符 _ 则可以避免这一告警。
5.where 条件
在部分循环遍历场景中,对于特定取值的迭代变量,我们可能需要直接跳过、进入下一轮循环,虽然可以使用 if 表达式和 continue 表达式在循环体中实现这一逻辑,但仓颉为此提供了更便捷的表达方式——可以在所遍历的“序列”之后用 where 关键字引导一个布尔表达式,这样在每次将进入循环体执行前,会先计算此表达式,如果值为 true 则执行循环体,反之直接进入下一轮循环。例如:
main() {
for (i in 0..8 where i % 2 == 1) { // i 为奇数才会执行循环体
println(i)
}
}
运行以上程序,将输出:
1
3
5
7
五、break 与 continue 表达式
在循环结构的程序中,有时我们需要根据特定条件提前结束循环或跳过本轮循环,为此仓颉引入了 break 与 continue 表达式,它们可以出现在循环表达式的循环体中,break 用于终止当前循环表达式的执行、转去执行循环表达式之后的代码,continue 用于提前结束本轮循环、进入下一轮循环。break 与 continue 表达式的类型都是 Nothing。
例如,以下程序使用 for-in 表达式和 break 表达式,在给定的整数数组中,找到第一个能被 5 整除的数字:
main() {
let numbers = [12, 18, 25, 36, 49, 55]
for (number in numbers) {
if (number % 5 == 0) {
println(number)
break
}
}
}
当 for-in 迭代至 numbers 数组的第三个数 25 时,由于 25 可以被 5 整除,所以将执行 if 分支中的 println 和 break,break 将终止 for-in 循环,numbers中的后续数字不会被遍历到,因此运行以上程序,将输出:
25
以下程序使用 for-in 表达式和 continue 表达式,将给定整数数组中的奇数打印出来:
main() {
let numbers = [12, 18, 25, 36, 49, 55]
for (number in numbers) {
if (number % 2 == 0) {
continue
}
println(number)
}
}
在循环迭代中,当 number 是偶数时,continue 将被执行,这会提前结束本轮循环、进入下一轮循环,println 不会被执行,因此运行以上程序,将输出:
25
49
55
资料来源:HarmonyOS Developer 官方网站