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