Edit Page

Ranges

Range expressions are formed with rangeTo functions that have the operator form .. which is complemented by in and !in. Range is defined for any comparable type, but for integral primitive types it has an optimized implementation. Here are some examples of using ranges

if (i in 1..10) { // equivalent of 1 <= i && i <= 10
    println(i)
}

Integral type ranges (IntRange, LongRange, CharRange) have an extra feature: they can be iterated over. The compiler takes care of converting this analogously to Java's indexed for-loop, without extra overhead.

for (i in 1..4) print(i) // prints "1234"

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

What if you want to iterate over numbers in reverse order? It's simple. You can use the downTo() function defined in the standard library

for (i in 4 downTo 1) print(i) // prints "4321"

Is it possible to iterate over numbers with arbitrary step, not equal to 1? Sure, the step() function will help you

for (i in 1..4 step 2) print(i) // prints "13"

for (i in 4 downTo 1 step 2) print(i) // prints "42"

To create a range which does not include its end element, you can use the until function:

for (i in 1 until 10) { // i in [1, 10), 10 is excluded
     println(i)
}

How it works

Ranges implement a common interface in the library: ClosedRange<T>.

ClosedRange<T> denotes a closed interval in the mathematical sense, defined for comparable types. It has two endpoints: start and endInclusive, which are included in the range. The main operation is contains, usually used in the form of in/!in operators.

Integral type progressions (IntProgression, LongProgression, CharProgression) denote an arithmetic progression. Progressions are defined by the first element, the last element and a non-zero step. The first element is first, subsequent elements are the previous element plus step. The last element is always hit by iteration unless the progression is empty.

A progression is a subtype of Iterable<N>, where N is Int, Long or Char respectively, so it can be used in for-loops and functions like map, filter, etc. Iteration over Progression is equivalent to an indexed for-loop in Java/JavaScript:

for (int i = first; i != last; i += step) {
  // ...
}

For integral types, the .. operator creates an object which implements both ClosedRange<T> and *Progression. For example, IntRange implements ClosedRange<Int> and extends IntProgression, thus all operations defined for IntProgression are available for IntRange as well. The result of the downTo() and step() functions is always a *Progression.

Progressions are constructed with the fromClosedRange function defined in their companion objects:

IntProgression.fromClosedRange(start, end, step)

The last element of the progression is calculated to find maximum value not greater than the end value for positive step or minimum value not less than the end value for negative step such that (last - first) % step == 0.

Utility functions

rangeTo()

The rangeTo() operators on integral types simply call the constructors of *Range classes, e.g.:

class Int {
    //...
    operator fun rangeTo(other: Long): LongRange = LongRange(this, other)
    //...
    operator fun rangeTo(other: Int): IntRange = IntRange(this, other)
    //...
}

Floating point numbers (Double, Float) do not define their rangeTo operator, and the one provided by the standard library for generic Comparable types is used instead:

    public operator fun <T: Comparable<T>> T.rangeTo(that: T): ClosedRange<T>

The range returned by this function cannot be used for iteration.

downTo()

The downTo() extension function is defined for any pair of integral types, here are two examples:

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)
}

reversed()

The reversed() extension functions are defined for each *Progression classes, and all of them return reversed progressions.

fun IntProgression.reversed(): IntProgression {
    return IntProgression.fromClosedRange(last, first, -step)
}

step()

step() extension functions are defined for *Progression classes, all of them return progressions with modified step values (function parameter). The step value is required to be always positive, therefore this function never changes the direction of iteration.

fun IntProgression.step(step: Int): IntProgression {
    if (step <= 0) throw IllegalArgumentException("Step must be positive, was: $step")
    return IntProgression.fromClosedRange(first, last, if (this.step > 0) step else -step)
}

fun CharProgression.step(step: Int): CharProgression {
    if (step <= 0) throw IllegalArgumentException("Step must be positive, was: $step")
    return CharProgression.fromClosedRange(first, last, if (this.step > 0) step else -step)
}

Note that the last value of the returned progression may become different from the last value of the original progression in order to preserve the invariant (last - first) % step == 0. Here is an example:

(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]