会社でKotlin dojo(第36回)を開催した

今週は第36回を開催しました。

 

前回の様子はコチラ↓

kdnakt.hatenablog.com

 

 

[第36回の様子]

2020/10/21に第36回を開催した。

 

参加メンバーは自分をいれて3名。3人いるとそこそこワイワイできて良い。

 

勉強会本編の内容としては、練習問題集Kotlin Koansの第6章 Genericsの最後の1問を終わらせた。進捗は100%となり、前回比+2%。長かったKoansも、ようやく全問解き終わった!やったー!!🎉

 

Koansが終わってしまったので、次回からやることを相談した。相談といっても、あまり時間もアイデアもなかったので、勉強会の当初の予定であったKotlin Hands-Onのどれかを進めることになった。

 

[学んだことや疑問点]

  • Generic function
    • partition()という、コレクションを新しい2つのコレクションに分割する関数がある
    • それと類似するpartitionTo()関数を実装せよ、という課題。分割後のコレクションを引数に受け取るあたりが異なる点。
    • 開始地点として与えられている情報は、以下の通り。
// これが実装すべき課題
fun partitionTo() = TODO()

// これがpartitionTo()の利用想定の一つ。
// もう一つコレクションの型が異なる例もあり、ジェネリクスを使えと要求されている
val (words, lines) = listOf("a", "a b", "c", "d e").
    partitionTo(ArrayList(), ArrayList()) { s -> !s.contains(" ") }
    • まず最初に、partitionTo()シグネチャを定義する部分で時間がかかってしまった
    • ジェネリクスを使うのでfun <T> Collection<T>.partitionTo(c1: Collection<T>, c2: Collection<T>, predicate: T -> Boolean)のような感じになることは想像がついた
    • ハマったポイントの一つとしては、渡された引数のコレクションに元のコレクションの中身を追加していかなければいけないので、引数の型はMutableCollectionになる点
    • もう一つのハマりポイントは、ラムダ式を型として宣言する場合に、引数が一つでも()を省略できないという点
    • 上記2つを解消して、とりあえずこうなった
fun <T> Collection<T>.partitionTo(
    c1: MutableCollection<T>,
    c2: MutableCollection<T>,
    predicate: (T) -> Boolean
)
    • しかしこれではpartitionTo()の戻り値の型が宣言されていないのが問題となる
    • 修正するとこうなる
fun <T> Collection<T>.partitionTo(
    c1: MutableCollection<T>,
    c2: MutableCollection<T>,
    predicate: (T) -> Boolean
): Pair<MutableCollection<T>, MutableCollection<T>>
    • とても長ったらしい...
    • 問題文のヒントに、toCollection()という組み込み関数のシグネチャを参考にしろとあるので、参照すると以下のようになっていた。なるほど!
fun <T, C: MutableCollection<in T>> Array<out T>.toCollection(
    destination: C
): C
    • これを参考に書き直すと、以下のようになった。MutableCollection<T>を3個省略できたのでだいぶすっきり。
fun <T, C: MutableCollection<in T>> Collection<T>.partitionTo(
    c1: C, c2: C, predicate: (T) -> Boolean
): Pair<C, C>
    • あとは、レシーバのコレクションを引数で渡されたコレクションにそれぞれ分割する関数本体を実装するだけ。
    • せっかくpartition()関数があるのだから、と自分が雑な実装で以下を提案した
fun <T, C: MutableCollection<in T>> Collection<T>.partitionTo(
    c1: C, c2: C, predicate: (T) -> Boolean
): Pair<C, C> {
    val (a, b) = this.partition(predicate)
    c1.addAll(a)
    c2.addAll(b)
    return Pair(c1, c2)
}
    • これでとりあえずテストは通ったのでよし。
    • あとで模範回答をみてみると、以下のように自分の回答よりもメモリ効率がいい感じになっていた。なるほど!!
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)
}
    • 最初のジェネリクスの宣言のところで、律儀にMutableCollection<in T>と書いていたが、模範回答を参照するに、inの部分は必須ではないようだ。なるほどー。

  

[まとめ]

今回も引き続き練習問題集Kotlin Koansを進めた。

今回は2回「やったー!」を実践した。関数定義が出来上がってテストが失敗するコードが書けた時点で初回の「やったー」を実践したが、この時点で時間の9割を消費していた……。

来週からはハンズオンをやっていくぞ!!