Creating Sequences

Builders

Example

Infinite sequences are fine as long as you limit them with take or first.

fun fibonacci() = sequence {
    var a = 0L
    var b = 1L
    while (true) {
        yield(a)
        val next = a + b
        a = b
        b = next
    }
}

fun main() {
    println(sequenceOf("a", "b", "c").toList())
    println(generateSequence(1) { it * 2 }.take(10).toList())
    println(fibonacci().take(12).toList())

    // generateSequence stops when the function returns null
    val countdown = generateSequence(5) { if (it > 1) it - 1 else null }
    println(countdown.toList())

    // yieldAll and yield mixed
    val pages = sequence {
        yield("cover")
        yieldAll((1..3).map { "page $it" })
        yield("back")
    }
    println(pages.joinToString(" | "))
}

Output

[a, b, c]
[1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
[5, 4, 3, 2, 1]
cover | page 1 | page 2 | page 3 | back

Stateful Operations

sorted(), sortedBy and distinct on a sequence must look at all elements before emitting the first one, so they lose most of the laziness benefit. windowed, chunked and zipWithNext stay lazy.

Example

fun main() {
    val readings = generateSequence(20) { it + listOf(1, -2, 3, 0, 2)[it % 5] }
    println(readings.take(8).toList())
    println(readings.take(8).zipWithNext { a, b -> b - a }.toList())
    println(readings.take(8).windowed(3) { it.average() }.map { "%.1f".format(it) }.toList())
    println(readings.take(8).chunked(3).toList())
}

Output

[20, 21, 19, 21, 19, 21, 19, 21]
[1, -2, 2, -2, 2, -2, 2]
[20.0, 20.3, 19.7, 20.3, 19.7, 20.3]
[[20, 21, 19], [21, 19, 21], [19, 21]]