2.8.1 什么是区间?
区间(Range)用来表示一个有固定步长的数字序列。简单说,就是"从哪开始、到哪结束、一次跳几步"。
你可以把区间想象成爬楼梯——你知道从第几级台阶开始、在第几级停下、一次跨几级。比如"从第 0 级开始,到第 10 级结束,一次跨 1 级",就是区间 0..=10 : 1。
最常见的就是整数区间,例如 1 到 5 的整数序列:
let r = 1..=5 : 1 // 包含 1, 2, 3, 4, 5
2.8.2 区间的三个要素
每个区间都有三个核心参数:
| 参数 | 名称 | 含义 | 示例 |
|---|---|---|---|
start |
起始值 | 序列从哪里开始 | 0 |
end |
终止值 | 序列到哪里结束 | 10 |
step |
步长 | 每两个元素之间差多少 | 1(每次+1)、-2(每次-2) |
step 固定是 Int64 类型,且 不能等于 0。如果步长为 0,程序会报错。
2.8.3 用构造函数创建区间
Range<T> 是仓颉的泛型区间类型。构造函数的完整参数如下:
Range<T>(start: T, end: T, step: Int64, hasStart: Bool, hasEnd: Bool, isClosed: Bool)
后面三个 Bool 参数的含义:
| 参数 | 为 true 时 |
为 false 时 |
|---|---|---|
hasStart |
包含开始值 | 不包含开始值 |
hasEnd |
包含结束值 | 不包含结束值 |
isClosed |
左闭右闭 | 左闭右开 |
最常用的组合是 hasStart = true, hasEnd = true,即"既有起始又有终止":
let r1 = Range<Int64>(0, 10, 1, true, true, true) // 闭区间:0 到 10(包含 10)
// 结果:0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
let r2 = Range<Int64>(0, 10, 1, true, true, false) // 开区间:0 到 10(不包含 10)
// 结果:0, 1, 2, 3, 4, 5, 6, 7, 8, 9
let r3 = Range<Int64>(10, 0, -2, true, true, false) // 从 10 开始,步长为 -2,到 0(不包含 0)
// 结果:10, 8, 6, 4, 2
构造函数参数多、限制也多,日常编码中几乎不用,推荐使用下面介绍的区间字面量,更简洁直观。
当 hasStart 或 hasEnd 为 false 时,在 for-in 中无效(效果等同二者均为 true);在与数组下标配合使用时,对应参数失效,改用数组的 0 或 size - 1 替代。
main() {
// hasEnd = false,在 for-in 中等同 true,范围为 1..=4
let r4 = Range<Int64>(1, 4, 1, true, false, true)
println(r4) // 输出:1 2 3 4
// 在数组切片中,hasEnd = false 使用 arr.size - 1 作为 end(即 6) 区间搭配数组使用案例请看上一章节
let arr = [1, 2, 3, 4, 5, 6, 7]
println(arr[r4]) // 输出:[2, 3, 4, 5, 6, 7]
}
2.8.4 用字面量创建区间(推荐)
仓颉提供了两种简洁的区间写法:
左闭右开区间 `start..end : step`
格式:start..end : step
含义:从 start 开始,到 end 之前(不包含 end)为止。
口诀:.. 看到终点就刹车,不到终点 🛑let r1 = 0..10 : 1 // 结果:0, 1, 2, 3, 4, 5, 6, 7, 8, 9
// 注意:没有 10!
左闭右闭区间 `start..=end : step`
格式:start..=end : step
含义:从 start 开始,到 end 为止(包含 end)。
口诀:..= 一路到终点,刚好赶上 🎯let n = 10
let r2 = 0..=n : 1 // 结果:0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
// 注意:包含 10!
对比一下
| 字面量 | 包含起始 | 包含终止 | 示例 | 结果 |
|---|---|---|---|---|
0..10 : 1 |
✅ 包含 0 | ❌ 不包含 10 | 0,1,2,3,4,5,6,7,8,9 |
共 10 个 |
0..=10 : 1 |
✅ 包含 0 | ✅ 包含 10 | 0,1,2,3,4,5,6,7,8,9,10 |
共 11 个 |
步长为负数的情况
步长也可以是负数,这样序列就是从大到小递减:
let r3 = 10..0 : -2 // 从 10 开始,每次减 2,到 0(不包含 0)
// 结果:10, 8, 6, 4, 2
let r4 = 10..=0 : -2 // 从 10 开始,每次减 2,到 0(包含 0)
// 结果:10, 8, 6, 4, 2, 0
步长为负数时,起始值要大于终止值,这样才有意义。如果 10..0 : 1(步长为正但你从大往小跑),那就是空区间了。
2.8.5 默认步长
步长 step 可以省略,省略时默认等于 1:
let r5 = 0..10 // 等价于 0..10 : 1
// 结果:0, 1, 2, 3, 4, 5, 6, 7, 8, 9
但是步长不能为 0:
let r6 = 0..10 : 0 // ❌ 报错:step cannot be 0(步长不能为 0)
2.8.6 空区间
有些情况下,区间里一个元素都没有,这就是空区间。什么时候会出现空区间?
口诀:步长方向与大小方向不一致时,就是空的。
// 以下都是空区间(不包含任何元素):
let r7 = 10..0 : 1 // 步长是 +1,但 start=10 > end=0,越走越远
let r8 = 0..10 : -1 // 步长是 -1,但 start=0 < end=10,也越走越远
let r9 = 10..=0 : 1 // 闭区间也一样,方向不对就是空
let r10 = 0..=10 : -1 // 同上,方向反了
空区间的判定规则:
| 区间形式 | 空区间条件 |
|---|---|
start..end : step(左闭右开) |
step > 0 且 start >= end |
start..end : step(左闭右开) |
step < 0 且 start <= end |
start..=end : step(左闭右闭) |
step > 0 且 start > end |
start..=end : step(左闭右闭) |
step < 0 且 start < end |
注意左闭右开和左闭右闭的细微区别:
- 左闭右开
start..end:start >= end就空(步长为正时) - 左闭右闭
start..=end:start > end才空(步长为正时),因为start = end时至少有一个元素
2.8.7 区间的实际用途
区间最常见的用途是配合 数组切片 和 for 循环:
配合数组切片
let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
let slice = arr[0..5] // 取下标 0 到 4,结果是 [0, 1, 2, 3, 4]
arr[n..] 和 arr[..n] 这类省略起始或结束的切片写法仅限数组等下标访问类型使用,不可用于 for-in 语句。其中 arr[n..] 等价于 arr[n..arr.size],arr[..n] 等价于 arr[0..n]。
配合 for 循环
main() {
for (i in 1..=5) { // i 依次取 1, 2, 3, 4, 5
println("第 ${i} 次循环")
}
}
运行结果:
第 1 次循环
第 2 次循环
第 3 次循环
第 4 次循环
第 5 次循环
利用区间做循环,比手动写起始和结束条件要清晰得多!
2.8.8 小结
| 概念 | 要点 |
|---|---|
| 区间的作用 | 表示一个有固定步长的序列 |
| 三个要素 | start(起始)、end(终止)、step(步长) |
| 左闭右开 | start..end : step,不包含 end |
| 左闭右闭 | start..=end : step,包含 end |
| 默认步长 | 省略 step 时默认为 1 |
| 步长不能为 0 | 否则报错 |
| 空区间 | 步长方向和大小方向不一致时出现 |
2.8.9 动手试一试 ✏️
- 用区间字面量表示 "从 2 到 20 的所有偶数"。
- 用区间字面量表示 "从 100 到 0 的所有 10 的倍数"(包含 0)。
- 下面的区间分别包含哪些数字?
5..15 : 35..=15 : 320..10 : -520..=10 : -5
- 以下哪个区间是空的?为什么?
0..100 : -1100..0 : -15..=5 : 1
点击查看答案
第 1 题:
let even = 2..=20 : 2 // 2, 4, 6, 8, 10, 12, 14, 16, 18, 20
第 2 题:
let tens = 100..=0 : -10 // 100, 90, 80, 70, 60, 50, 40, 30, 20, 10, 0
第 3 题:
| 区间 | 包含的数字 |
|---|---|
5..15 : 3 |
5, 8, 11, 14(不包含 15) |
5..=15 : 3 |
5, 8, 11, 14(包含 15 但 15 不在序列里,因为 5+3×3=14,下一个是 17 > 15) |
20..10 : -5 |
20, 15, 10(不包含 10?注意左闭右开,不包含 10,所以是 20, 15) |
20..=10 : -5 |
20, 15, 10(包含 10) |
5..=15 : 3 虽然标记为"包含 15",但实际序列是 5, 8, 11, 14。这是否包含 15 看的是"是否在 end 处停止"——14 之后下一个是 17,已经超过 15 了,所以 15 并不在序列中。当 start + k×step 恰好等于 end 时,闭区间才会包含它(如 0..=10 : 1 包含 10)。
第 4 题:
0..100 : -1→ 空区间。步长为负(-1),但起始 0 < 终止 100,方向相反。100..0 : -1→ 非空。步长为负(-1),起始 100 > 终止 0,方向一致。结果:100, 99, 98, ..., 2, 1(不含 0)。5..=5 : 1→ 非空。左闭右闭且 start = end,包含一个元素 5。
2.8.10 常见问题
Q1:`0..10` 和 `0..=10` 有什么区别?
0..10 是左闭右开,包含 0 但不包含 10,结果是 0,1,2,...,9(共 10 个)。0..=10 是左闭右闭,包含 0 和 10,结果是 0,1,2,...,10(共 11 个)。
Q2:step 可以是小数吗?
不可以。step 的类型固定为 Int64,只能是整数。如果你想表示小数的等差数列,需要用其他方式实现。
Q3:空区间有什么实际用途吗?
空区间通常不是有意创建的,而是边界情况。在循环或数组切片中,如果区间是空的,就不会执行任何操作,避免了程序崩溃。例如 arr[5..3] 会返回空数组而不是报错。
Q4:区间和数组有什么区别?
区间描述的是一个规则序列(从哪开始、到哪结束、步长多少),它本身不存储所有元素的值,而是按需计算。而数组是真实存着所有元素的集合。区间更像是一个"生成规则",数组才是"存储容器"。
