The basic variant of the generateSequence() function is declared as follows:
fun <T : Any> generateSequence(nextFunction: () -> T?): Sequence<T>
It takes one parameter called nextFunction, which is a function that returns the next elements of the sequence. Under the hood, it is being invoked by the Iterator.next() function, inside the Sequence class' internal implementation, and allows instantiation of the next object to be returned while consuming the sequence values.
In the following example, we are going to implement a finite sequence that emits integers from 10 to 0:
var counter = 10
val sequence: Sequence<Int> = generateSequence {
counter--.takeIf { value: Int -> value >= 0 }
}
print(sequence.toList())
The takeIf() function applied to the current counter value checks whether its value is greater or equal to 0. If the condition is fulfilled, it returns the counter value; otherwise, it returns null. Whenever null is returned by the generateSequence() function, the sequence stops. After the takeIf function returns the value, the counter value is post-decremented. The preceding code will result in the following numbers being printed to the console:
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
The subsequent values of the Fibonacci sequence are generated by summing up their two preceding ones. Additionally, the two first values are equal to 0 and 1. In order to implement such a sequence, we are going to use an extended variant of the generateSequence() function with an additional seed parameter, declared as follows:
fun <T : Any> generateSequence(seed: T?, nextFunction: (T) -> T?): Sequence<T>