目次
Kotlin の while
, for
では break
, continue
が使えますが、forEach
, repeat
では使えません。
環境
- Kotlin 1.3.11
まずは、 forEach
, repeat
で break
, continue
を使いたい場合の簡単な対処方法を紹介します。 そのあとでなぜこうなっているのかを説明します。
簡単な対処方法
forEach
, repeat
は いずれも for
で書き換えられるので、書き換えてしまえば break
, continue
が使えます。
前 | 後 | |
---|---|---|
forEach |
something.forEach |
for (it in something) |
repeat |
repeat (time) |
for (it in time) |
なぜ break
, continue
が使えないのか
(現実をいってしまえば Kotlin がそう作られているからなのですが、) while
, for
と forEach
, repeat
には大きな違いがあります。
while
,for
は Kotlin の構文forEach
,repeat
は Kotlin の関数
似た記法になっているのでわかりにくくなっていますが、 while
, for
は構文として、 break
, continue
が使えるように設計されています。 一方で、 forEach
, repeat
は Kotlin の標準ライブラリで定められた高階関数です。
1 2 3 |
releat (3) { // this is function } |
つまり { ... }
は関数です。
関数の中で break
や continue
が出てくるのは変ですね。 break
, continue
は使えません。
イテレーションをコントロールする他の方法
forEach
, repeat
などの高階関数でブロック部分を抜けるには、 return
を使います。 関数ですから return
で終了するのは自然に思えますね。
しかし、 return
をそのまま使うと、 呼び出し元の関数を終了してしまいます。
1 2 3 4 5 6 7 |
fun something() { listOf(1, 2, 3).forEach { println(it) return // something を終了する } println("finish") } |
ラベルを使うとこれを簡単に制御できます。
continue のような処理
ラムダを使う場合
return@ラムダのラベル
でラムダの continue
を行うことができます。
1 2 3 4 5 6 7 8 |
fun something() { listOf(1, 2, 3).forEach { println(it) return@forEach // ブロック内の処理はここまで println(it) } println("finished") } |
このラベルは自分でつけることもできて ラベル名@{ ... }
のように記述します。
もし forEach が2重だったら
2重で forEach
がある場合は、ラベルを自分でつけます。 次のコードは外側のループに対して continue
を行なっています。
1 2 3 4 5 6 7 8 9 10 |
fun something() { listOf(1, 2, 3).forEach outer@{ a -> listOf(4, 5, 6).forEach { b -> print(a) return@outer println(b) } } println("finished") } |
無名関数を使う場合
ラムダを無名関数に変えれば、 単純に return
と書くだけで同じことができます。
1 2 3 4 5 6 7 |
fun something() { listOf(1, 2, 3).forEach(fun(value: Int) { println(value) return // ブロック内の処理はここまで println(it) }) } |
break のような処理
continue と同じようにラベルを使うのですが、 外側にスコープ関数をつけます。
ラムダを使う場合
外側をスコープ関数の run
で囲んで、 そのラムダを抜けるように return@run
と記述します
1 2 3 4 5 6 7 8 9 10 |
fun something() { run { listOf(1, 2, 3).forEach { println(it) return@run // ループはここでおわり println(it) } } println("finished") } |
見てわかるように、 ラムダは break
を前提に作られていません。
無名関数を使う場合
ラムダの場合と同じようにラベルを使います。
1 2 3 4 5 6 7 8 9 |
fun something() { var breakFlag = false listOf(1, 2, 3).forEach(fun(value) { println(value) return@run // ループはここでおわり println(value) }); println("finished") } |
break, continue のまとめ
上記をまとめると次のようになります。
break | continue | |
ラムダ | スコープ関数とラベル | ラベル |
---|---|---|
無名関数 | return |