[毎日Kotlin] Day5.Lambdas(ラムダ式)
はじめに
毎日Kotlinシリーズです。
このシリーズを初めての方はこちらです。「毎日Kotlin」はじめました | Developers.IO
問題
コレクション(リスト)の中に偶数があるかどうかをチェックする関数を作ってみよう。
Kotlin supports a functional style of programming. Read about higher-order functions and function literals (lambdas) in Kotlin.
Pass a lambda to any function to check if the collection contains an even number. The function any gets a predicate as an argument and returns true if there is at least one element satisfying the predicate.
fun containsEven(collection: Collection<Int>): Boolean = collection.any { TODO() }
補足、anyは標準ライブラリーで提供されており、定義は以下の通り。
any/_Collections.kt at 1.2.0 · JetBrains/kotlin
//何か一つでもpredicateの条件と一致しているものがあったら、return true をするもの public inline fun <T> Iterable<T>.any(predicate: (T) -> Boolean): Boolean { if (this is Collection && isEmpty()) return false for (element in this) if (predicate(element)) return true return false }
狙い
ここで考えて欲しい問題の意図はなんだろうか?
JavaでもJava8が使えるようになりました。ラムダ式はAndroidでも頻繁に使うことになるので、ぜひ一度やっていただきたい問題です。コールバックやDSLなどに幅広い用途で使用されています。読みやすさの反面、ラムダ式に慣れていないと読みづらいと感じてしまいKotlinに抵抗感を感じる原因になってしまいます。
ラムダ式は怖くない!便利!読みやすい!チャレンジ!
解答例
fun containsEven(collection: Collection<Int>): Boolean = collection.any { it % 2 == 0 }
何も省略しない形
省略されている部分があるので、まず省略されていない形をみてみましょう。
fun containsEven(collection: Collection<Int>): Boolean = collection.any({ value: Int -> value % 2 == 0 })
anyの引数は、predicate: (T) -> Booleanのラムダになっています。Tを引数にとってBooleanを返すラムダをとるようになっています。今回のTはInt型が必要ですね。変数名がありません。変数名は呼び出しのタイミングでつけます。今回は勝手にvalueという名前にをつけました。呼び出す際にわかりやすい変数名につけてあげましょう。
ラムダ式の引数の型を省略
containsEvenの引数collectionは、Collection
//何か一つでもpredicateの条件と一致しているものがあったら、return true をするもの public inline fun <T> Iterable<T>.any(predicate: (T) -> Boolean): Boolean { if (this is Collection && isEmpty()) return false for (element in this) if (predicate(element)) return true return false }
ラムダ式の引数の型省略ができます。
fun containsEven(collection: Collection<Int>): Boolean = collection.any({ value -> value % 2 == 0 })
ラムダ式の引数の変数名を省略
ラムダ式の引数の個数が1つの場合は、itという変数名をデフォルトでつけてくれます。変数を省略できます。
fun containsEven(collection: Collection<Int>): Boolean = collection.any({ it % 2 == 0 })
()を省略
anyの最後の引数がpredicate: (T) -> Booleanとラムダを引数になっています。引数の最後がラムダの場合()を省略することができます。
fun containsEven(collection: Collection<Int>): Boolean = collection.any{ it % 2 == 0 }
余談
ラムダ式は、無名関数のシンタックスシュガー(のはず)なので、無名関数で書くと以下の通りです。
fun containsEven(collection: Collection<Int>): Boolean = collection.any(fun(value: Int): Boolean { return value % 2 == 0 })
あとがき
あまり詰め込みすぎるのも良くないので、今日はここまで。ラムダ式を覚えるとKotlinのサンプルが読めるようになります。ラムダは頻繁に使われるのでたくさん試しましょう。例えば一つ例に出します。
Androidで使われる例
button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.v(TAG, "clicked"); } });
View.OnClickListenerは(v: View) -> Unitのラムダと同じとみれる(SAM変換)。
button.setOnClickListener { Log.v(TAG, "clicked") }
( Kotlinでリスナーやコールバックをスッキリと書く【関数リテラルとSAM変換】 - Qiita参考)
Day6.でまたお会いしましょう。