【Kotlin練習問題】forループのためのイテレーターの実装

イテレーター(iterator)とは

イテレーターとはforループが動作するために必要な概念で、イテレーターが実装されていないオブジェクトではforループを使うことができません。

具体的にはイテレーターとは次の要素を返すnext()メソッドと次の要素が存在するかを確かめるhasNext()メソッドに分解することができます。これらを実装することで、forループがいつまで繰り返されるべきなのかを知ることができるのです。

Kotlinにおけるfor構文は以下のようになります。

for (item in collection) print(item)
for (i in 1..3) {
    println(i) //1,2,3
}

上の1,2,3が出力されるforループでは、1..3というIntの値の範囲を指定するIntRange型にイテレーターが実装されているため、1の次には2があり、2の次には3があり、3の次には値がないといった判断をコンピューターができるのです。

従って、独自に作ったクラスでforループを使いたい場合、自身でイテレーターを実装する必要があります。

問題

以下のDateRangeクラスにIterable<MyDate>を実装し、繰り返し処理ができるようにしてください。
なお、DateUtil.ktファイルに既にMyDate.followingDate()という関数が定義されているので、次の日を見つけるロジックを自身で実装する必要はありません。

DateUtil.ktとMyDate.ktは下にあります。

class DateRange(val start: MyDate, val end: MyDate) //ここにIterable<MYDate>を実装する

fun iterateOverDateRange(firstDate: MyDate, secondDate: MyDate, handler: (MyDate) -> Unit) {
    for (date in firstDate..secondDate) {
        handler(date)
    }
}

DateUtil.kt

import java.uti.Calendar

fun MyDate.folowwingDate(): MyDate {
    val c = Calendar.getInstance()
    c.set(year, month, dayOfMonth)
    val millisecondsInADay = 24 * 60 * 60 * 1000L
    val timeInMillis = c.timeInMillis + millisecondsInADay
    val resuly = Calendar.getInstance()
    result.timeInMillis = timeInMillis
    return MyDate(result.get(Calendar.YEAR), result.get(Calendar.MONTH), result.get(Calendar.DATE))
}

MyDate.kt

data class MyDate(val year: Int, val month: Int, val dayOfMonth: Int) : Comparable<MyDate> {
    override fun compareTo(other: MyDate): Int {
        if (year != other.year) return year - other.year
        if (month != other.month) return month - other.month
        return dayOfMonth - other.dayOfMonth
    }
}

operator fun MyDate.rangeTo(other: MyDate) = DateRange(this, other)
[expander_maker id=”1″ more=”ヒント” less=”非表示”]

ヒント

イテレーターを実装するクラスはIterable<T>を継承している必要があります。

今回の場合、TはMyDateになるので、

class DateRange(val start: MyDate, val end: MyDate) : Iterable<MyDate>のようになります。

イテレーターを実装するには、クラスの中でIterator<MyDate>を返すiterator()関数をオーバーライドし、その関数内で次の値を返すnext()と、次の値の有無をチェックするhasNext()を実装します。

next()内では、次の値がある場合には次の値を返し、ない場合には例外を投げるようにしましょう。

[/expander_maker] [expander_maker id=”1″ more=”答え” less=”非表示”]

答え

class DateRange(val start: MyDate, val end: MyDate) : Iterable<MyDate> {
    override fun iterator(): Iterator<MyDate> {
        return object : Iterator<MyDate> {
            var current: MyDate = start

            override fun next(): MyDate {
                if (!hasNext()) throw NoSuchElementException()
                val result = current
                current = current.followingDate()
                return result
            }

            override fun hasNext(): Boolean = current <= end
        }
    }
}
fun iterateOverDateRange(firstDate: MyDate, secondDate: MyDate, handler: (MyDate) -> Unit) {
    for (date in firstDate..secondDate) {
        handler(date)
    }
}

[解説]

DateRangeというIterable<MyDate>を継承したクラスでMyDateのイテレーターを実装しています。

iterator()関数では範囲内最初の日付であるstartをcurrentに代入し、next()内で次の日付があればcurrentに代入しています。次の日付の取得には既に定義されたfollowingDate()関数を使用しています。

hasNext()ではcurrentがendを超えた場合にfalseを返すように設定されているので、next()が繰り返し使用され、currentの値がendを超えた時にfalseになります。

イテレーターの実装は次の値を取得するnext()と次の値があるかを確かめるhasNext()の実装と考えましょう。

[/expander_maker]

その他の問題はこちらからどうぞ。

完全無料で通えるプログラミングスクール

プログラミング学習はどうしても一人だとつまづいてしまう時がきます。調べればわかることも少なくないですが、最初のうちは調べ方もわからないことが多いため、あまり効率的ではありません。

効率的かつ挫折せずにプログラミングを学習したい方はスクールを検討してみるのも一つの手です。

中には無料で通えるスクールや、就職保証をしてくれるスクールなどもあるので、きっとあなたの目的に応じて最適のスクールが見つかります!以下の記事で評判がよく特におすすめのスクールをいくつかピックアップしているので、スクール選びで後悔したくない方は御覧ください!

https://codelabsjp.net/best-programming-school/

おすすめ書籍

Kotlinの文法をまず学びたい!という方には以下の書籍がおすすめです。Kotlinは日本語書籍がまだ豊富とは言えない状況ですが、細かく解説されており、Kotlin入門者のかたでもつまずくことなく学習できると思います。

[itemlink post_id=”1743″]

実際にアプリを作りながら覚えていきたい!という方には以下もお勧めです。はじめに上の書籍で文法をさらっと学んでから取り組むのがお勧めです。

[itemlink post_id=”1745″]