LoginSignup
6
2

More than 5 years have passed since last update.

例で理解するSequence<T>のfilterやmap

Last updated at Posted at 2017-04-02

はじめに

 KotlinのSequenceは、次のようなIterator<T>を返すiterator()関数を持つインターフェースです。

public interface Sequence<out T> {
    public operator fun iterator(): Iterator<T>
}

 kotlin.sequencesパッケージには、Sequenceを生成する関数と多くのSequence拡張関数が定義されています。拡張関数の中にはfiltermaptakeといった物があります。

 Listにもmapfiltertakeがありますね。

 この投稿では、Listのmapやfilter、takeとの違いを通して、Sequenceのmapとfilter、takeの挙動を紹介します。

List<T>のfilter・map・take

 Listfiltermaptakeの挙動を見てみましょう。

 filterは選択を、mapは射影を行う関数です。takeは先頭から指定した要素数のList<T>を新たに生成する関数です。

 実際のコードではfilterやmapに渡す関数リテラル内部で副作用のあるprintlnを行うことは良くありません。今回は挙動を紹介するために関数リテラル内でprintlnを使います。

Listのfilter・mapの例
    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は要素が必要になったタイミングで、必要な分の要素に対して射影の処理・選択の処理が行われます。

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2