|The Programmers Guide To Kotlin- Iterators & Sequences|
|Written by Mike James|
|Monday, 17 September 2018|
Page 1 of 3
Iterators and sequences are a large part of using collections and taking a functional approach to programming. We look at how they work in Kotlin.
Programmer's Guide To Kotlin
Now Available as a Print Book
You can buy it from: Amazon
Some Chapters Already Available On The Web
It is assumed that you know the basics of collections and in particular how Kotlin presents collections to the user. This is covered in detail in the first part of this chapter. Here we are only concerned with iterators and the related idea of a sequence.
Collections of objects usually have an iterator to allow you to step through each object in turn. In fact, you can have an iterator that isn't associated with a collection of objects and simply generates the next object on demand.
An iterator has to have at least two functions – next which returns the next object, and hasNext which returns true if there is a next object.
For example the Kotlin for can be written as:
The two loops work in the same way. The iterator method returns an initialized iterator for the collection. You can use this to retrieve each element in turn. Notice that you can't reset an iterator to the first value – you simply create a new instance. If this behavior doesn't suit you then simply include a start and/or end parameter in the constructor and modify the Next and hasNext methods accordingly.
Although iterators are generally linked to collection style data structures, they can also be used to generate sequences. In the case of a collection, the Next method retrieves the next item in the collection, but for a sequence it simply computes the next value.
For example a CountToTen class would be something like:
and it could be used anywhere you needed the sequence of numbers. For example:
prints 1 to 10.
In most cases it would be better to create a class that implemented the Iterable interface. This has just one operator method, iterator, which returns an Iterator object for the class in question.
Notice that an iterator is "lazy" in the sense that it doesn't compute the complete sequence of values at once, it only computes a value when needed. Kotlin has a lot of facilities for functional programming and in functional programming you often chain together functions like iterators which produce sequences.
For efficiency, it is important that these put off creating a sequence until it is actually needed – i.e. they need to be lazy in an extended sense. Kotlin provides the Sequence<T> type to allow you to use and implement iterators that are lazy in this broader sense.
Although functional programming isn't the focus of this book, it is worth giving an example of the way iterators and sequences differ, if only because non-functional programmers find it hard to understand why there are two so closely related types.
which uses a sequence and:
which uses the iterator associated with the List.
The map method simply applies the specified function, i.e. the println, to each of the elements of the sequence or collection. If you run this you will discover that the map acting on the sequence doesn't print anything, but the List iterator does. The reason is that in the case of the sequence, map returns an unevaluated sequence ready for further operations. In the case of the List, the map returns another List after evaluating everything. If you want to force the sequence to evaluate, you have to do something that makes use of its results.
You will now see printed:
This unexpected order of events, i.e. the map happens after the “end” happens because the map isn’t evaluated until it is actually needed. So the assignment to ms doesn’t evaluate the map, it is simply stored ready to be evaluated when needed. Then “end” is printed and then the map is finally evaluated as its last element is needed. The evaluation is even more lazy than you might imagine in that that if you use first in place of last then only the first element is evaluated. The lazy evaluation is only performed to the point where the element that is actually needed becomes available.
This is aggressively lazy and it has to be to ensure that many functional forms of simple algorithms are practical.
|Last Updated ( Monday, 17 September 2018 )|