Range Over Iterators in Kotlin

Starting with version 1.23, Go has added support for iterators, which lets us range over pretty much anything!

Let’s look at the List type from the previous example again. In that example, we had an AllElements method that returned a slice of all elements in the list. With Kotlin iterators (known as sequences), we can do it better - as shown below.

Code Example

class List<T> {
    private var head: Element<T>? = null
    private var tail: Element<T>? = null

    private class Element<T>(val value: T, var next: Element<T>? = null)

    fun push(value: T) {
        if (tail == null) {
            head = Element(value)
            tail = head
        } else {
            tail!!.next = Element(value)
            tail = tail!!.next
        }
    }

    fun all(): Sequence<T> = sequence {
        var current = head
        while (current != null) {
            yield(current.value)
            current = current.next
        }
    }
}

fun genFib(): Sequence<Int> = sequence {
    var a = 1
    var b = 1
    while (true) {
        yield(a)
        val next = a + b
        a = b
        b = next
    }
}

fun main() {
    val lst = List<Int>()
    lst.push(10)
    lst.push(13)
    lst.push(23)

    for (e in lst.all()) {
        println(e)
    }

    val all = lst.all().toList()
    println("all: $all")

    for (n in genFib()) {
        if (n >= 10) break
        println(n)
    }
}

Explanation

Define the List Class We start by defining a generic List class to store elements.

class List<T> {
    private var head: Element<T>? = null
    private var tail: Element<T>? = null

    private class Element<T>(val value: T, var next: Element<T>? = null)
    
    // Other methods...
}

Push Method The push method adds an element to the end of the list.

fun push(value: T) {
    if (tail == null) {
        head = Element(value)
        tail = head
    } else {
        tail!!.next = Element(value)
        tail = tail!!.next
    }
}

All Method The all method returns a Kotlin sequence, which is an iterator that lazily evaluates the elements in the list.

fun all(): Sequence<T> = sequence {
    var current = head
    while (current != null) {
        yield(current.value)
        current = current.next
    }
}

Generate Fibonacci Sequence We define a genFib function that generates an infinite sequence of Fibonacci numbers using a Kotlin sequence.

fun genFib(): Sequence<Int> = sequence {
    var a = 1
    var b = 1
    while (true) {
        yield(a)
        val next = a + b
        a = b
        b = next
    }
}

Main Function In the main function, we create a List, add elements to it, and then iterate over them using the all method.

fun main() {
    val lst = List<Int>()
    lst.push(10)
    lst.push(13)
    lst.push(23)

    for (e in lst.all()) {
        println(e)
    }

    val all = lst.all().toList()
    println("all: $all")

    for (n in genFib()) {
        if (n >= 10) break
        println(n)
    }
}

This code demonstrates how to iterate over custom data structures using Kotlin sequences, providing a modern and idiomatic way to handle iteration in Kotlin.