おはようエンジニア

まともなエンジニアになりたい

『Kotlinイン・アクション』第5章を読む (その2)

第5章 ラムダを使ったプログラミングの続きです。

『Kotlinイン・アクション』第5章を読む (その1) - おはようエンジニア

遅延評価

  • 先行評価
    • コレクション操作 (前回記事で登場した map や filter など)
    • 操作をチェインしたときの中間結果は一時的なリストに保持される
  • 遅延評価
    • シーケンス の操作 (APIはコレクションと同じ)
    • 一時オブジェクトを生成しないで計算できる
    • 要素が多いコレクションをチェインする場合、計算効率を高めるためにシーケンスに変換して処理する
    • 要素を順次処理するだけの場合に使えるが、要素の参照などはできない
people.asSequence()  // シーケンスに変換
        .map(Person:name)
        .filter { it.startWith("A") }
        .toList()  // リストに変換

シーケンスの操作

  • 中間操作と終端操作に分かれている
    • 中間操作: シーケンスを変換する (遅延実行される)
      • 例: map, filter など
    • 終端操作: 遅延されていた中間操作をまとめて実行して結果を返す
      • 例: find, any, sum など

処理の順序

  • 先行評価は全要素の操作が完了してから次の操作に移る
    • 操作の順番がパフォーマンスに影響する
  • 遅延評価は要素を取り出して全操作を完了してから次の要素に移る
    • 要素の順番がパフォーマンスに影響する
    • find の呼び出しなど、特定の述部を満たす要素が見つかり次第処理を終了できる

シーケンスの生成

  • generateSequence関数でシーケンスを生成する
    • 1つ前の要素から次の要素を計算する
    • 階層構造を順にたどって処理していくときなどに便利
fun File.isInsideHiddenDirectory() = 
        generateSequence(this) { it.parentFile }.any { it.isHidden }

SAMインターフェイス

// Java
button.setOnClickListener (new OnClickListener() {
    @Override
    public void onClick(View v) {
    ...
    }
}
// Kotlin
button.setOnClickListener { view -> ... }
  • ラムダの代わりに明示的に匿名オブジェクトを渡すこともできる

SAM変換

// SAMコンストラクタでインスタンスを取得する
val listener = OnClickListener { view -> 
    ...
}
// 再利用する
button1.setOnClickListener(listener)

レシーバ付きラムダ

with関数

  • 第1引数に変数、第2引数にラムダをとる関数
    • ラムダの中で第1引数をレシーバとして使える (this参照できる)
    • 返り値はラムダの最後の式の結果
  • 用途
    • あるインスタンスが持ついくつかの異なるメソッドを呼び出すような場合
fun alphabet(): String {
    val stringBuilder = StringBuilder()
    return with(stringBuilder) {
        // ラムダ内では this が stringBuilder になる
        for (letter in 'A'..'Z') {
            append(letter)
        }
        toString()
    }
}

apply関数

  • with関数とほぼ同じ
    • 返り値はレシーバオブジェクト
    • 拡張関数として定義されている
  • 用途
    • インスタンスを生成してからすぐに初期化処理をするような場合 (Builderなど)
    • 関数を式本体スタイルにする場合に便利
fun alphabet() = StringBuilder().apply {
    // ラムダ内では this が stringBuilder になる
    for (letter in 'A'..'Z') {
        append(letter)
    }
}.toString()

まとめ

この章ではラムダを使ってコードを簡潔にする例がたくさん紹介されていました。

特にラムダの用途の代表であるコレクションの操作は実際にコードで頻出するので、使うときには以下の2点に気をつけようと思います。

  • 不要な一時変数を宣言していないか
  • 式本体スタイルに書き直すことができるか
  • 処理の中で同じインスタンス名が何度も登場していないか

ラムダをうまく使ってKotlinらしい書き方を身に着けていきたいです。