Kotlin KoansでKotlinを勉強したメモをまとめています。
問題
リンクは以下。
https://play.kotlinlang.org/koans/Generics/Generic%20functions/Task.kt
少し簡略化して説明すると、以下のコードが動くように、リストに入った要素を条件によって2つに分けるpartitionTo()メソッドを実装するというものです。
val (words, lines) = listOf("a", "a b", "c", "d e")
.partitionTo(ArrayList(), ArrayList()) { s -> !s.contains(" ") }
check(words == listOf("a", "c"))
check(lines == listOf("a b", "d e"))
解答と解説
まず、解答例は以下です。
fun <T, C : MutableCollection<T>> Collection<T>.partitionTo(first: C, second: C, predicate: (T) -> Boolean): Pair<C, C> {
for (element in this) {
if (predicate(element)) {
first.add(element)
} else {
second.add(element)
}
}
return Pair(first, second)
}
このコードはジェネリックを学習するのが目的ですが、Kotlinの初心者からすると何点かポイントがあるので解説します。
1. 拡張関数について
Kotlin Koansにも登場しますが、Kotlinには拡張関数という機能があり、別のクラスにメソッドを追加することができます。
この解答例では、CollectionクラスにpartitonToメソッドを追加しています。
2. ジェネリックについて
partitionTo()は3つの引数を受け取ります。
始めの2つはArrayListなどの配列型で、その要素の型は何でもOKです。
3つめの要素は関数ですが、その引数は↑の要素の方と同じである必要があります。
つまりpartitionTo()の定義の中で、型が一位に定まらないものが2つあります。そのため、以下のように記述する必要があります。
<T, C>
さらに、TはpartitionTo()の第1・2引数で使われ、Cはその要素、および第3引数の関数の引数です。
TがCのコレクション型であることを明記するため
<T, C : MutableCollection<T>>
という記述をしています。
3. 引数の関数を()の外に出す
partitionToを実行するコードで
partitionTo(ArrayList(), ArrayList()) { s -> !s.contains(" ") }
のように書いていましたが、これは
partitionTo(ArrayList(), ArrayList(), { s -> !s.contains(" ") } )
のように書くのと同じです。
これは、引数の最後が関数の場合にのみできる書き方です。
ちなみに、関数を引数に渡す書き方はとっつきにくいですが、自分は以下のサイトが分かりやすいなと思いました。 https://pouhon.net/kotlin-higher-order-function/1342/