Kotlin Reference: Ranges

  可以使用 rangeTo() 函数(对应操作符为 ..)编写区间表达式(Range Expression),并使用 in!in 来补充。区间可以用于所有可比较的类型,但对于整型基本类型(Integral Primitive Type)的实现有优化。下面是一些使用区间的例子:

[code lang=”kotlin”]if (i in 1..10) { // equivalent of 1 &lt= i && i &lt= 10
println(i)
}[/code]

  整型区间(IntRangeLongRangeCharRange)有一个额外的功能:它们可以被迭代。编译器会把迭代转换为类似 Java 中基于索引的 for 循环的形式,不会带来额外开销。

[code lang=”kotlin”]for (i in 1..4) print(i) // prints “1234”

for (i in 4..1) print(i) // prints nothing[/code]

  逆序迭代一组数字也很简单,可以使用标准库中的 downTo() 函数:

[code lang=”kotlin”]for (i in 4 downTo 1) print(i) // prints “4321”[/code]

  如果想用 1 以外的任意步长来迭代数字,可以使用 step() 函数:

[code lang=”kotlin”]for (i in 1..4 step 2) print(i) // prints “13”

for (i in 4 downTo 1 step 2) print(i) // prints “42”[/code]

  使用 until 函数可以创建不包含结尾元素的区间:

[code lang=”kotlin”]for (i in 1 until 10) { // i in [1, 10), 10 is excluded
println(i)
}[/code]

How it works

  区间实现了库中的一个公共接口:ClosedRange<T>

  ClosedRange<T> 表示数学意义上的闭区间,用于可比较类型。它有两个端点,startendInclusive,二者都包含在区间内。它主要的操作为 contains,通常以 in / !in 操作符的形式使用。

  整型数列(IntProgressionLongProgressionCharProgression)表示数学上的等差数列,数列由首元素(first)、尾元素(last)和一个非零的步长(step)定义。第一个元素是 first,后续元素是前一个元素加上 steplast 元素总会在迭代中出现,除非数列是空的。

  数列是 Iterable<N> 的子类,这里的 NintLongChar。所以数列可以用于 for 循环和 mapfilter 等函数。对 Progression 进行迭代等同于 Java / JavaScript 中的基于索引的 for 循环:

[code lang=”java”]for (int i = first; i != last; i += step) {
// …
}[/code]

  对于整型类型,.. 操作符会创建一个实现了 ClosedRange<T> 并继承了 *Progression 的对象。比如 IntRange 实现了 ClosedRange<Int> 并继承了 IntProgression,因此适用于 IntProgression 的操作也适用于 IntRangedownTo()step() 的结果始终是 *Progression

  通过在数列的伴生对象中定义的 fromClosedRange 函数来构造数列:

[code lang=”kotlin”]IntProgression.fromClosedRange(start, end, step)[/code]

  数列中的 last 元素是计算得到的,满足 (last - first) % step == 0:对于正的 steplast 的值为满足条件的不大于 end 的最大值,;对于负的 steplast 的值为满足条件的不小于 end 的最小值。

Utility functions

rangeTo()

  rangeTo()操作符用于整型类型上时,只是简单地调用了对应 *Range 类的构造器,如:

[code lang=”kotlin”]class Int {
//…
operator fun rangeTo(other: Long): LongRange = LongRange(this, other)
//…
operator fun rangeTo(other: Int): IntRange = IntRange(this, other)
//…
}[/code]

  浮点类型数字(DoubleFloat)没有定义自己 rangeTo 操作符,而是使用标准库为泛用 Comparable 类型提供的 rangeTo 作为替代:

[code lang=”kotlin”]public operator fun > T.rangeTo(that: T): ClosedRange[/code]

该函数返回的区间不能用于迭代。

downTo()

  对于任意两个整型类型的组合,都有对应的 downTo() 扩展函数,如下面的两个例子:

[code lang=”kotlin”]fun Long.downTo(other: Int): LongProgression {
return LongProgression.fromClosedRange(this, other.toLong(), -1L)
}

fun Byte.downTo(other: Int): IntProgression {
return IntProgression.fromClosedRange(this.toInt(), other, -1)
}[/code]

reversed()

  对于每一个 *Progression 类,都定义了 reversed() 扩展函数,用于返回逆序的数列。

[code lang=”kotlin”]fun IntProgression.reversed(): IntProgression {
return IntProgression.fromClosedRange(last, first, -step)
}[/code]

step()

  对于每一个 *Progression 类,都定义了 step() 扩展函数,用于根据 step(函数参数)的值返回具有对应步长的数列。步长值必须为正数,因此这个函数永远不会改变迭代的方向:

[code lang=”kotlin”]fun IntProgression.step(step: Int): IntProgression {
if (step &lt= 0) throw IllegalArgumentException(“Step must be positive, was: $step”)
return IntProgression.fromClosedRange(first, last, if (this.step &gt 0) step else -step)
}

fun CharProgression.step(step: Int): CharProgression {
if (step &lt= 0) throw IllegalArgumentException(“Step must be positive, was: $step”)
return CharProgression.fromClosedRange(first, last, if (this.step &gt 0) step else -step)
}[/code]

  需要注意的是,为了保证不变式 (last - first) % step == 0,该函数返回数列的 last 可能会与原数列的 last 不同,例如:

[code lang=”kotlin”](1..12 step 2).last == 11 // progression with values [1, 3, 5, 7, 9, 11]
(1..12 step 3).last == 10 // progression with values [1, 4, 7, 10]
(1..12 step 4).last == 9 // progression with values [1, 5, 9][/code]