はじめに
KotlinのSequenceは、次のようなIterator<T>
を返すiterator()
関数を持つインターフェースです。
public interface Sequence<out T> {
public operator fun iterator(): Iterator<T>
}
kotlin.sequencesパッケージには、Sequenceを生成する関数と多くのSequence拡張関数が定義されています。拡張関数の中にはfilterやmap、takeといった物があります。
この投稿では、Listのmapやfilter、takeとの違いを通して、Sequenceのmapとfilter、takeの挙動を紹介します。
List<T>
のfilter・map・take
Listのfilter、map、takeの挙動を見てみましょう。
filterは選択を、mapは射影を行う関数です。takeは先頭から指定した要素数のList<T>
を新たに生成する関数です。
実際のコードではfilterやmapに渡す関数リテラル内部で副作用のあるprintln
を行うことは良くありません。今回は挙動を紹介するために関数リテラル内でprintlnを使います。
val list : List<Int> = listOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
val converted : List<String> = list
.filter { // 偶数だけに選択
println("filter $it")
it.rem(2) == 0
}
.map { // 文字列型に射影
println("map $it")
it.toString()
}
.take(3) // 戦闘から3個だけのList<T>に
println("--------")
for(it in converted) println("for $it")
上のコードの実行結果は次の通りです。
filter 0
filter 1
filter 2
filter 3
filter 4
filter 5
filter 6
filter 7
filter 8
filter 9
map 0
map 2
map 4
map 6
map 8
--------
for 0
for 2
for 4
まず、filterが実行されます。すべての要素に対してfilterに渡した関数リテラルが適用されていますね。
次に、mapが実行されます。filterした結果のListにmapに渡した関数リテラルが適用されていますね。
そして、takeが実行されます。しかし特に何も表示されません。
そしてfor文の前のprintlnが実行され、--------
が表示されています。
最後にfor文が実行されます。
List<T>
のfilterはその場で全要素を選択し、mapはその場で全要素を射影します。
Sequence<T>
のfilter・map・take
それでは同様のコードのSequence<T>
版を見てみましょう。
val list : Sequence<Int> = sequenceOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
val converted : Sequence<String> = list
.filter {
println("filter $it")
it.rem(2) == 0
}
.map {
println("map $it")
it.toString()
}
.take(3)
println("--------")
for(it in converted) println("for $it")
実行結果は次の通りです。
--------
filter 0
map 0
for 0
filter 1
filter 2
map 2
for 2
filter 3
filter 4
map 4
for 4
Listのfilter・map・takeとの実行結果と大きく違うことに気が付きましたか?
--------
がまず表示されていますね。表示された結果から、filterやmapに渡した関数リテラルの適用は--------
を表示された後にされていますね。
Sequence<T>
のfilter・map・takeは、すべての要素に対して一気に行われるわけではありません。その要素が必要になったタイミングで実行されるのです。
それでは、見ていきましょう。
まず、filterが実行されます。しかし特に表示はされていません。各要素に対しての選択はまだ実行されていません。
次に、mapが実行されます。しかし特に表示はされていません。各要素に対しての射影はまだ実行されていません。
そして、takeが実行されます。しかし特に表示はされません。
そしてfor文の前のprintlnが実行され、--------
が表示されています。
最後にfor文が実行されます。
for文で要素の列挙を始められ、要素が必要になります。そして先頭からfilter・map・takeが実行されます。
まとめ
List<T>
のmapやfilterはその場で全要素を射影・選択が実行されます。
それとは異なり、Sequence<T>
のmapやfilterは要素が必要になったタイミングで、必要な分の要素に対して射影の処理・選択の処理が行われます。