Edited at

[Scala] 無限ループ内で break を使わない方法を考える

More than 1 year has passed since last update.

ポエムではないです。が思考の経過を垂れ流してます。

scala では break 制御構文が使えませんが、無限ループ内で、値を生成してその値をループの外で使いたいなんてことがたまにあります。

今回はシーケンスの中から使われていないユニークな値を探してそれを使うケースで考えます。

ちなみに、ライブラリで提供されている break を利用することはできますが、例外を発生させてそれをキャッチして処理するというのは個人的に好きじゃないので別の方法を考えます。(好みです(^^;)

まず単純に考えると、ループの継続を行うかどうかを変数に入れて制御する以下の方法があるかと思います。

var loop: Boolean = true

var candidate: Int = 0
while(loop) {
candidate = Random.nextInt()
if (seq.indexOf(candidate) == -1)
loop = false
}

この場合、loop, candidate がミュータブルになっているのでなんとなくバグが入り込みそうで嫌な感じです。

この var を取り除くために無限ループ部分を関数にして値が見つかり次第 return してしまう方法が思いつきます。

def scanUniqValue(seq: Seq[Int]): Int = {

while(true) {
val candidate = Random.nextInt()
if (seq.indexOf(candidate) == -1)
return candidate
}
sys.error("never reach")
}

val x = scanUniqValue(seq)

この場合、最後に型を合わせるために、関数の最後にダミーの値を返すか例外を発生させないと型が合わずコンパイルができません。

これもまたなんか気持ち悪いです。。。

次に・・・再帰はパスします・・・w

scala なんだしもうちょっとなんかあるんでないのかと思って試行錯誤した結果、最終的に行き着いた方法がこれ。

Iterator.continually(Random.nextInt()).find(seq.indexOf(_) == -1).get

Iterator.continually で無限に値を生成しつつ、find で一番最初に見つかった値を取得しています。

値の生成部分と検索の部分が明確に分かれてて理解しやすいし個人的にはこれが一番スッキリかなぁ。

他に良い書き方があればコメントください。

以上です。