KotlinDive

プログラミング言語 Kotlin についての入門ブログです。

ジャンプ

ジャンプとは

ジャンプとは、分岐や繰り返しなどの制御フローの途中に挿入することで、 関数やループを途中で抜けたり、あるいは特定の条件のときにループをスキップしたりすることができます。
Kotlinには制御をジャンプするために、次の式が用意されています。

説明
return デフォルトでは、returnを含む最も内側の関数、匿名関数から制御を返す
break そのbreakを含む最も内側のループを終了する
continue そのcontinueを含む最も内側のループに対して、そのループをスキップして次のループにすすめる
 fun main(args: Array) {
    // ブレーク、コンティニューの条件をラムダ式で指定する
    val isBreak = { i: String -> i == "BreakStr" }
    val isContinue = { i: String -> i == "ContinueStr"}

    testLoop(null, isBreak, isContinue)
    testLoop(listOf("suzuki", "tanaka", "BreakStr", "yamada"), isBreak, isContinue)
    testLoop(listOf("suzuki", "tanaka", "ContinueStr", "yamada"), isBreak, isContinue)
}

fun testLoop(list: List?, isBreak: (String) -> Boolean, isContinue: (String) -> Boolean) {
    println("-------------------")
    if (list == null) {
        // listがnullの場合はこの関数を抜ける
        println("Return!")
        return
    }

    for (i in list) {
        if (isBreak(i)) {
            // ブレーク条件をみたす場合は、ループを終了する
            println("Break!")
            break
        }

        if (isContinue(i)) {
            // コンティニュー条件を満たす場合は、今回分の処理をスキップして、次の分の処理に進む
            println("Continue!")
            continue
        }

        println(i)
    }
}
 -------------------
Return!
-------------------
suzuki
tanaka
Break!
-------------------
suzuki
tanaka
Continue!
yamada

ラベル付きbreak/continue

ループにラベルをつけることで、breakやcontimueで特定のラベルのループを抜けることができます。 ラベル@ラベル名で指定します。 これは、多重にネストしたループを一気に抜けたりする場合に便利です

 fun main(args: Array) {
    val data = listOf(1, 2, 3,
                            4, 5, 6,
                            7, 0, 8,
                            9, 10, 11,
                            12, -1, 13,
                            14, 15, 16)

    labelLoop(data, 3, 6)
}

fun labelLoop(data: List, width: Int, height: Int) {
    if (data.size != width * height) return

    outerLoop@ for (y in 0 until height) {
        for (x in 0 until width) {
            val d = data[x + y * width]
            if (d < 0) break@outerLoop
            if (d == 0) {
                print("\n")
                continue@outerLoop
            }

            print("${d} ")
        }

        print("\n")
    }
}
 1 2 3 
4 5 6 
7 
9 10 11 
12 

ラベル付きreturn

Kotlinでは、関数はネスト可能です。 通常のreturnは、そのreturnを囲む一番内側の(匿名)関数をリターンします。
次の例を見てみましょう。

 fun main(args: Array) {
    outerFunc()
}

fun outerFunc() {
    println("Called outerFunc")

    fun innerFunc(value: Int) {
        println("   Called innerFunc")

        if (value < 0) {
            println("   Return innerFunc")
            // このリターンはinnerFuncを終了する
            // outerFuncは終了しない
            return
        }

        println("   End of innerFunc")
    }

    innerFunc(10)
    innerFunc(-5)

    println("End of outerFunc")
}

この、そのreturnを囲むというのが特に重要な意味を持つのは、 ラムダ式の中にreturnがある場合です。 次のサンプルの動作を確認してみましょう。

 fun main(args: Array) {
    outerFunc(listOf("suzuki", "yamada", "ReturnStr", "tanaka"))
}

fun outerFunc(list: List) {
    println("Called outerFunc")

    val outerVal = "outerFuncで宣言された変数"
    list.also {
        println("ラムら式はクロージャなので、${outerVal}にアクセスできる。")
    }.forEach {
        // このreturnは、outerFuncを直接リターンする
        if (it == "ReturnStr") return
        println(it)
    }

    println("End of outerFunc")
}
 Called outerFunc
ラムら式はクロージャなので、outerFuncで宣言された変数にアクセスできる。
suzuki
yamada

もし、ラムダ式の中のreturnラムダ式をリターンしたい場合は、ラベルを付ける必要があります。

 fun main(args: Array) {
    outerFunc(listOf("suzuki", "yamada", "ReturnStr", "tanaka"))
}

fun outerFunc(list: List) {
    println("Called outerFunc")

    val outerVal = "outerFuncで宣言された変数"
    list.also {
        println("ラムら式はクロージャなので、${outerVal}にアクセスできる。")
    }.forEach returnLamda@{
        // このreturnは、このラムダ式だけをリターンできる
        if (it == "ReturnStr") return@returnLamda
        println(it)
    }

    println("End of outerFunc")
}
 Called outerFunc
ラムら式はクロージャなので、outerFuncで宣言された変数にアクセスできる。
suzuki
yamada
tanaka
End of outerFunc

上記の例のように、明示的にラベルを作成しなくても、暗黙的ラベルを利用するとより簡単にリターンすることもできます。

 fun main(args: Array) {
    outerFunc(listOf("suzuki", "yamada", "ReturnStr", "tanaka"))
}

fun outerFunc(list: List) {
    println("Called outerFunc")

    val outerVal = "outerFuncで宣言された変数"
    list.also {
        println("ラムら式はクロージャなので、${outerVal}にアクセスできる。")
    }.forEach {
        // 暗黙的ラベルを利用する
        if (it == "ReturnStr") return@forEach
        println(it)
    }

    println("End of outerFunc")
}
 Called outerFunc
ラムら式はクロージャなので、outerFuncで宣言された変数にアクセスできる。
suzuki
yamada
tanaka
End of outerFunc