Kotlin: なぜ while, for では break, continue が使えて forEacn, repeat では使えないのか


Kotlin の while, for では break, continue が使えますが、forEach, repeat では使えません。

環境

  • Kotlin 1.3.11

まずは、 forEach, repeatbreak, continue を使いたい場合の簡単な対処方法を紹介します。 そのあとでなぜこうなっているのかを説明します。

簡単な対処方法

forEach, repeat は いずれも for で書き換えられるので、書き換えてしまえば break, continue が使えます。

書き換え
forEach something.forEach for (it in something)
repeat repeat (time) for (it in time)

なぜ break, continue が使えないのか

(現実をいってしまえば Kotlin がそう作られているからなのですが、) while, forforEach, repeat には大きな違いがあります。

  • while, for は Kotlin の構文
  • forEach, repeat は Kotlin の関数

似た記法になっているのでわかりにくくなっていますが、 while, for構文として、 break, continue が使えるように設計されています。 一方で、 forEach, repeat は Kotlin の標準ライブラリで定められた高階関数です。

つまり { ... } は関数です。

関数の中で breakcontinue が出てくるのは変ですね。 break, continue は使えません。

イテレーションをコントロールする他の方法

forEach, repeat などの高階関数でブロック部分を抜けるには、 return を使います。 関数ですから return で終了するのは自然に思えますね。

しかし、 return をそのまま使うと、 呼び出し元の関数を終了してしまいます。

ラベルを使うとこれを簡単に制御できます。

continue のような処理

ラムダを使う場合

return@ラムダのラベル でラムダの continue を行うことができます。

このラベルは自分でつけることもできて ラベル名@{ ... } のように記述します。

もし forEach が2重だったら

2重で forEach がある場合は、ラベルを自分でつけます。 次のコードは外側のループに対して continue を行なっています。

無名関数を使う場合

ラムダを無名関数に変えれば、 単純に return と書くだけで同じことができます。

break のような処理

continue と同じようにラベルを使うのですが、 外側にスコープ関数をつけます。

ラムダを使う場合

外側をスコープ関数の run で囲んで、 そのラムダを抜けるように return@run と記述します

見てわかるように、 ラムダは break を前提に作られていません。

無名関数を使う場合

ラムダの場合と同じようにラベルを使います。

break, continue のまとめ

上記をまとめると次のようになります。

ラムダ、無名関数で break, continue を実現する方法
break continue
ラムダ スコープ関数とラベル ラベル
無名関数 return