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 |