The Programmers Guide To Kotlin- Iterators & Sequences |
Written by Mike James | |||
Wednesday, 24 August 2022 | |||
Page 1 of 2 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 Third Edition
You can buy it from: Amazon Contents
<ASIN:B0D8H4N8SK> 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. In chapter but not in this extract
IteratorsCollections 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. This means the Kotlin for can be written as: while(it.hasNext()){ For example: var a = mutableListOf<String>("a","b","c") 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: class CountToTen():Iterator<Int>{ and it could be used anywhere you needed the sequence of numbers. For example: val a=CountToTen() prints 123 ... 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. SequencesAlthough 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 such closely related types. Compare this code which uses a sequence: val s = sequenceOf(1,2,3,4,5,6,7,8,9,10) val ms = s.map({println(it);it}) println("end") with this using the iterator associated with the List: val i = listOf(1,2,3,4,5,6,7,8,9,10) val mi = i.map({println(it);it}) println("end") 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, for example: val s = sequenceOf( 1,2,3,4,5,6,7,8,9,10 ) You will now see printed: end 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. That is all you see is: end 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 ( Wednesday, 24 August 2022 ) |