Edit Page

Ranges and Progressions

Kotlin lets you easily create ranges of values using the rangeTo() function from the kotlin.ranges package and its operator form ... Usually, rangeTo() is complemented by in or !in functions.

if (i in 1..4) {  // equivalent of 1 <= i && i <= 4
    print(i)
}

Integral type ranges (IntRange, LongRange, CharRange) have an extra feature: they can be iterated over. These ranges are also progressions of the corresponding integral types. Such ranges are generally used for iteration in the for loops.

fun main() {
//sampleStart
    for (i in 1..4) print(i)
//sampleEnd
}

To iterate numbers in reverse order, use the downTo function instead of ...

fun main() {
//sampleStart
    for (i in 4 downTo 1) print(i)
//sampleEnd
}

It is also possible to iterate over numbers with an arbitrary step (not necessarily 1). This is done via the step function.

fun main() {
//sampleStart
    for (i in 1..8 step 2) print(i)
    println()
    for (i in 8 downTo 1 step 2) print(i)
//sampleEnd
}

To iterate a number range which does not include its end element, use the until function:

fun main() {
//sampleStart
    for (i in 1 until 10) {       // i in [1, 10), 10 is excluded
        print(i)
    }
//sampleEnd
}

Range

A range defines a closed interval in the mathematical sense: it is defined by its two endpoint values which are both included in the range. Ranges are defined for comparable types: having an order, you can define whether an arbitrary instance is in the range between two given instances. The main operation on ranges is contains, which is usually used in the form of in and !in operators.

To create a range for your class, call the rangeTo() function on the range start value and provide the end value as an argument. rangeTo() is often called in its operator form ...

class Version(val major: Int, val minor: Int): Comparable<Version> {
    override fun compareTo(other: Version): Int {
        if (this.major != other.major) {
            return this.major - other.major
        } else if (this.minor != other.minor) {
            return this.minor - other.minor
        } else return 0
    }
}

fun main() {
//sampleStart
    val versionRange = Version(1, 11)..Version(1, 30)
    println(Version(0, 9) in versionRange)
    println(Version(1, 20) in versionRange)
//sampleEnd
}

Progression

As shown in the examples above, the ranges of integral types, such as Int, Long, and Char, can be treated as arithmetic progressions of them. In Kotlin, these progressions are defined by special types: IntProgression, LongProgression, and CharProgression.

Progressions have three essential properties: the first element, the last element, and a non-zero step. The first element is first, subsequent elements are the previous element plus a step. The last element is always hit by iteration unless the progression is empty. Iteration over a progression with a positive step is equivalent to an indexed for loop in Java/JavaScript.

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

When you create a progression implicitly by iterating a range, this progression's first and last elements are the range's endpoints, and the step is 1.

fun main() {
//sampleStart
    for (i in 1..10) print(i)
//sampleEnd
}

To define a custom progression step, use the step function on a range.

fun main() {
//sampleStart
    for (i in 1..8 step 2) print(i)
//sampleEnd
}

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

To create a progression for iterating in reverse order, use downTo instead of .. when defining the range for it.

fun main() {
//sampleStart
    for (i in 4 downTo 1) print(i)
//sampleEnd
}

Progressions implement Iterable<N>, where N is Int, Long, or Char respectively, so you can use them in various collection functions like map, filter, and other.

fun main() {
//sampleStart
    println((1..10).filter { it % 2 == 0 })
//sampleEnd
}