スイッチ内からループを抜け出す方法は?


113

次のようなコードを書いています。

while(true) {
    switch(msg->state) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
    case DONE:
        break; // **HERE, I want to break out of the loop itself**
    }
}

それを行う直接的な方法はありますか?

フラグを使用して、スイッチの直後に条件付きブレークを置くことでループから抜けることができることは知っています。C ++にこのための構造が既にあるかどうかを知りたいだけです。


20
切り替え後に条件付きブレークが必要なのはなぜですか?while(true)からwh​​ile(flag)に変更してください
Tal Pressman

7
@Dave_Jarvisこれは、彼が何をしようとしているかを説明するために彼がここに入れた簡略版だと思います。
Alterlife 2009

6
あなたが数ページの長さの関数を生成するこれらのプログラマーの1人なら、goto魅力的で、時には唯一のクリーンな方法を見つけるでしょう。コードを数行だけの小さな関数に編成し、それぞれに1つのことを行う傾向がある場合、この問題に遭遇することは決してありません。(ちなみに、コードも読みやすくなります。)
sbi

13
最寄りの地下鉄駅への行き方を知りたいだけなのに、禁煙を勧められたくなったら。
マイケルクレリン-ハッカー、2009年

7
@hacker:ええと、煙のせいで目の前に地下鉄の駅が見えない場合、そのアドバイスはそれほど悪くないかもしれません。:)
sbi 2009

回答:


49

前提

次のコードは、言語や目的の機能に関係なく、不適切な形式と見なされます。

while( true ) {
}

サポートする引数

次のwhile( true )理由により、ループは不適切な形式です。

  • whileループの暗黙のコントラクトを解除します。
    • whileループ宣言では、唯一の終了条件を明示的に記述する必要があります。
  • それは永遠にループすることを意味します。
    • ループ内のコードは、終了句を理解するために読み取る必要があります。
    • 永久に繰り返されるループは、ユーザーがプログラム内からプログラムを終了できないようにします。
  • 非効率的です。
    • 「true」のチェックを含む、複数のループ終了条件があります。
  • バグが発生しやすい。
    • 各反復で常に実行されるコードを配置する場所を簡単に決定できません。
  • 不必要に複雑なコードにつながります。
  • 自動ソースコード分析。
    • バグの発見、プログラムの複雑さの分析、セキュリティチェック、またはコードを実行せずに他のソースコードの動作を自動的に導出するには、初期破壊条件を指定することで、アルゴリズムが有用な不変条件を特定できるようになり、自動ソースコード分析の指標が向上します。
  • 無限ループ。
    • while(true)もが無限ではないforループを常に使用する場合、ループに実際に終了条件がないときに簡潔に通信する能力を失います。(おそらく、これはすでに起こっているので、要点は議論の余地があります。)

「移動」の代わり

次のコードはより良い形式です:

while( isValidState() ) {
  execute();
}

bool isValidState() {
  return msg->state != DONE;
}

メリット

フラグなし。いいえgoto。例外なし。簡単に変更できます。読みやすい。簡単に修正できます。さらに、コード:

  1. ループ自体からループのワークロードの知識を分離します。
  2. コードを保守している人が機能を簡単に拡張できるようにします。
  3. 複数の終了条件を1つの場所に割り当てることができます。
  4. 実行するコードから終了句を分離します。
  5. 原子力発電所にとってより安全です。;-)

2番目のポイントは重要です。コードがどのように機能するかを知らずに、メインループに他のスレッド(またはプロセス)に一定のCPU時間を与えるように依頼された場合、2つの解決策が思い浮かびます。

オプション1

一時停止を簡単に挿入します。

while( isValidState() ) {
  execute();
  sleep();
}

オプション#2

上書き実行:

void execute() {
  super->execute();
  sleep();
}

このコードは、が埋め込まれたループよりも単純です(したがって、読みやすくなっています)switch。このisValidStateメソッドは、ループを継続するかどうかを決定するだけです。メソッドの主力は、メソッドに抽象化する必要がexecuteあります。これにより、サブクラスがデフォルトの動作をオーバーライドできるようになります(組み込みのswitchおよびを使用する難しいタスクgoto)。

Pythonの例

StackOverflowに投稿された次の回答(Pythonの質問に対する)と比較してください。

  1. 永久にループします。
  2. 選択肢を入力するようユーザーに依頼します。
  3. ユーザーの入力が「再開」の場合は、ループを永久に継続します。
  4. それ以外の場合は、ループを永久に停止します。
  5. 終わり。
コード
while True: 
    choice = raw_input('What do you want? ')

    if choice == 'restart':
        continue
    else:
        break

print 'Break!' 

対:

  1. ユーザーの選択を初期化します。
  2. ユーザーの選択が「再起動」という単語である間ループします。
  3. 選択肢を入力するようユーザーに依頼します。
  4. 終わり。
コード
choice = 'restart';

while choice == 'restart': 
    choice = raw_input('What do you want? ')

print 'Break!'

ここではwhile True、誤解を招く複雑なコードになります。


45
while(true)有害であるべきという考えは私には奇妙に思えます。実行前にチェックするループ、後でチェックするループ、途中でチェックするループがあります。後者にはCおよびC ++の構文構造がないため、while(true)またはを使用する必要がありfor(;;)ます。これを間違っていると考えると、さまざまな種類のループをまだ十分に検討していません。
sbi 2009

34
私が同意しない理由:while(true)とfor(;;;)は混乱しない((do {} while(true)とは異なり))、彼らは彼らが直接やっていることを述べているからです。実際には、任意のループ条件を解析してそれが設定されている場所を探すよりも「break」ステートメントを探す方が簡単であり、正確性を証明するのは難しくありません。コードをどこに置くかについての議論は、continueステートメントが存在する場合と同じように扱いにくいものであり、ifステートメントを使用した場合と比べてあまり良くありません。効率性の議論は確かにばかげています。
David Thornley、

23
「while(true)」が表示されるときはいつでも、任意の終了条件よりも難しくない内部終了条件を持つループを考えることができます。単純に、「while(foo)」ループ(fooは任意の方法で設定されたブール変数)は、ブレーク付きの「while(true)」と同じです。どちらの場合もコードを確認する必要があります。ちなみに、ばかげた理由(そして、マイクロ効率はばかげた議論です)を出すことは、あなたのケースを助けません。
David Thornley、

5
@Dave:理由を挙げました:これは、途中の状態をチェックするループを取得する唯一の方法です。古典的な例:while(true) {string line; std::getline(is,line); if(!is) break; lines.push_back(line);}もちろん、これを(std::getlineループ条件として使用して)前処理されたループに変換できますが、これにはそれ自体に欠点lineがあります(ループのスコープ外の変数である必要があります)。そして、もしそうなら、私は尋ねなければならないでしょう:なぜとにかく3つのループがあるのですか?すべてを常に前処理されたループに変換できます。最適なものを使用してください。while(true)合う場合は、それを使用してください。
sbi 2009

3
この答えを読んでいる人々への礼儀として、私は質問を生み出した財の質についてコメントすることを避け、質問自体に答えることに固執します。問題は次のバリエーションです。これは多くの言語の機能だと思います。ループが別のループ内にネストされています。あなたは内側のループから外側のループを抜け出したいと考えています。これはどのように行われますか?
Alex Leibowitz

172

使用できますgoto

while ( ... ) {
   switch( ... ) {
     case ...:
         goto exit_loop;

   }
}
exit_loop: ;

12
ただそのキーワードに熱中しないでください。
Fragsworth、2009

45
人々が気に入らないので、私は反対票を投じることは面白いgotoです。OPはフラグを使用せずに明確に述べました。OPの制限を考慮して、より良い方法を提案できますか?:)
Mehrdad Afshari 09/09/14

18
無知な後藤嫌いを補うためにちょうどtに賛成。Mehrdadは、ここで賢明な解決策を提案したことで、いくつかのポイントを失うことになることを知っていたと思います。
マイケルクレリン-ハッカー、2009年

6
+ 1、OPの制限を考慮したとしても、これ以上の方法はありません。フラグは醜く、ループ全体でロジックを分割するため、把握するのがより困難になります。
Johannes Schaub-litb 2009

11
結局のところ、gotoコンストラクトがないと、すべての言語が失敗します。高水準言語はそれを難読化しますが、内部で十分に見ていると、すべてのループが見つかります。for、while、until、switch、ifキーワードを使用して自分自身をだますが、最終的にはすべて、いずれかの方法でGOTO(またはJMP)を利用します。自分をだまして、あらゆる種類の巧妙な方法でそれを隠すことができます。あるいは、実用的に正直に、適切な場所でgotoを使用することもできます。
同期さ

57

別の解決策はcontinue、と組み合わせてキーワードを使用することですbreak。つまり、

for (;;) {
    switch(msg->state) {
    case MSGTYPE
        // code
        continue; // continue with loop
    case DONE:
        break;
    }
    break;
}

continueステートメントを使用して、ループを続行する各ケースラベルを終了し、ステートメントを使用して、ループをbreak終了する必要があるケースラベルを終了します。

もちろん、このソリューションは、switchステートメントの後に実行する追加のコードがない場合にのみ機能します。


9
これは確かに非常に洗練されていますが、ほとんどの開発者は、これがどのように/どのように機能するかを理解するために、最初に1分間見なければならないという欠点があります。:(
sbi 2009

8
最後の文は、これをそれほど素晴らしいものにしない:(理由です。
nawfal 2013年

考えてみると、良いコンピュータコードはありません。私たちはそれを理解するだけの訓練を受けています。たとえば、xmlは私が学ぶまでゴミのように見えました。今ではゴミの見た目が減っています。for(int x = 0; x <10; ++ x、moveCursor(x、y))は実際にプログラムで論理エラーを引き起こしました。最後に更新条件が発生したことを忘れていました。

22

これを行うためのきちんとした方法は、これを関数に入れることです:

int yourfunc() {

    while(true) {

        switch(msg->state) {
        case MSGTYPE: // ... 
            break;
        // ... more stuff ...
        case DONE:
            return; 
        }

    }
}

オプション(ただし、「悪い習慣」):すでに示唆したように、gotoを使用するか、スイッチ内で例外をスローできます。


4
例外は、よく知られた関数の動作ではなく、例外をスローするために使用する必要があります。
クレメントエレマン2009

私は来世に同意します:関数に入れます。
dalle

14
この場合、例外のスローは本当に悪です。「後藤を使いたいけど、どこかで使ってはいけないという本を読んでいるので、おかしなところに行って見栄えよく見せかける」という意味です。
Gorpik

例外のスローやgotoの使用はひどい考えだと私は同意しますが、それらは有効なオプションであり、そのように私の回答に記載されています。
Alterlife 2009

2
例外をスローすると、GOTOがCOMEFROMに置き換えられます。しないでください。gotoの方がずっとうまくいきます。
David Thornley、

14

私の知る限り、C ++には「ダブルブレーク」や同様の構成要素はありません。最も近いものはgoto-ですが、その名前には悪い意味が含まれていますが、ある理由で言語に存在しています-慎重にそして控えめに使用されている限り、それは実行可能なオプションです。


8

スイッチを次のような別の関数に入れることができます。

bool myswitchfunction()
{
    switch(msg->state) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
    case DONE:
        return false; // **HERE, I want to break out of the loop itself**
    }
    return true;
}

while(myswitchfunction())
    ;

7

gotoが気に入らない場合でも、例外を使用してループを終了しないでください。次のサンプルは、それがいかに醜いかを示しています。

try {
  while ( ... ) {
    switch( ... ) {
      case ...:
        throw 777; // I'm afraid of goto
     }
  }
}
catch ( int )
{
}

この回答のgotoように使用します。この場合、コードは他のどのオプションよりも明確になります。この質問がお役に立てば幸いです。goto

しかしgoto、ここでは文字列のため、使用することが唯一のオプションだと思いますwhile(true)。ループのリファクタリングを検討する必要があります。次の解決策を考えます:

bool end_loop = false;
while ( !end_loop ) {
    switch( msg->state ) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
    case DONE:
        end_loop = true; break;
    }
}

または次のように:

while ( msg->state != DONE ) {
    switch( msg->state ) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
}

1
ええ、でもは例外がもっと好きではありません;-)
quamrana

3
例外を使用してgotoをエミュレートすることは、もちろんgotoを実際に使用するよりも悪いです!また、おそらくかなり遅くなります。
ジョンカーター、

2
@Downvoter:それは私が述べた、例外を使用してはいけない、またはリファクタリングを提案したからですか?
キリルV.リヤドビンスキー2009

1
反対投票者ではない-しかし...ループを終了するために例外を使用するという考えを提案しているのか非難しているのか、あなたの答えは明確ではありません。たぶん最初の文は次のようになるはずです:「あなたが気に入らなくても、ループを抜けるために例外をgoto使わないでください:」?
ジョナサンレフラー、

1
@ジョナサンレフラー、ありがとう、貴重なコメントのサンプルを見せてくれました。コメントを考慮して回答を更新しました。
キリルV.リヤドビンスキー09/09/14

5

この場合、ループから抜け出すためのC ++構造はありません。

フラグを使用してループを中断するか、(必要に応じて)コードを関数に抽出してを使用しますreturn


2

gotoを使用することもできますが、ループを停止するフラグを設定することをお勧めします。次に、スイッチから抜け出します。


2

whileループの条件を修正して、問題を解消してみませんか?

while(msg->state != DONE)
{
    switch(msg->state) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
    case DONE:
        // We can't get here, but for completeness we list it.
        break; // **HERE, I want to break out of the loop itself**
    }
}

2

いいえ、キーワード "break"がスイッチブロックを終了するためにすでに予約されている場合、C ++にはこのための構成はありません。あるいは、終了フラグを指定したdo..while()で十分です。

do { 
    switch(option){
        case 1: ..; break;
        ...
        case n: .. ;break;
        default: flag = false; break;
    }
} while(flag);

1

おもう;

while(msg->state != mExit) 
{
    switch(msg->state) 
    {
      case MSGTYPE: // ...
         break;
      case DONE:
      //  .. 
      //  ..
      msg->state =mExit;
      break;
    }
}
if (msg->state ==mExit)
     msg->state =DONE;

0

それを行う最も簡単な方法は、SWITCHを実行する前に単純なIFを置くことです。そのIFは、ループを終了するための条件をテストします..........可能な限り単純です


2
ええと...その条件をwhile引数に入れると、もっと簡単になるでしょう。つまり、それが目的です!:)
マークA.ドノホー

0

breakC ++ のキーワードは、最もネストされた囲み反復またはswitchステートメントのみを終了します。したがって、ステートメントwhile (true)内でループを直接抜けることはできませんswitch。ただし、次のコードを使用することもできます。これは、この種の問題の優れたパターンだと思います。

for (; msg->state != DONE; msg = next_message()) {
    switch (msg->state) {
    case MSGTYPE:
        //...
        break;

    //...
    }
}

msg->state等しいときに何かを行う必要がある場合DONE(クリーンアップルーチンを実行するなど)、forループの直後にそのコードを配置します。つまり、現在以下の場合:

while (true) {
    switch (msg->state) {
    case MSGTYPE:
        //... 
        break;

    //...

    case DONE:
        do_cleanup();
        break;
    }

    if (msg->state == DONE)
        break;

    msg = next_message();
}

次に代わりに使用します:

for (; msg->state != DONE; msg = next_message()) {
    switch (msg->state) {
    case MSGTYPE:
        //...
        break;

    //...
    }
}

assert(msg->state == DONE);
do_cleanup();

0

説明の深さを考えると、これがいかに単純であるかに驚かされます。

bool imLoopin = true;

while(imLoopin) {

    switch(msg->state) {

        case MSGTYPE: // ... 
            break;

        // ... more stuff ...

        case DONE:
            imLoopin = false;
            break;

    }

}

笑!!本当に!それだけで十分です!1つの追加変数!


0
while(MyCondition) {
switch(msg->state) {
case MSGTYPE: // ... 
    break;
// ... more stuff ...
case DONE:
   MyCondition=false; // just add this code and you will be out of loop.
    break; // **HERE, you want to break out of the loop itself**
}
}

0

同じ問題が発生し、フラグを使用して解決しました。

bool flag = false;
while(true) {
    switch(msg->state) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
    case DONE:
        flag = true; // **HERE, I want to break out of the loop itself**
    }
    if(flag) break;
}

-2

C ++構文をよく覚えている場合はbreak、と同じように、ステートメントにラベルを追加できますgoto。だからあなたが望むものは簡単に書かれるでしょう:

while(true) {
    switch(msg->state) {
    case MSGTYPE: // ...
        break;
    // ... more stuff ...
    case DONE:
        break outofloop; // **HERE, I want to break out of the loop itself**
    }
}

outofloop:
// rest of your code here

1
残念ながら、あなたの記憶は不完全です。それは、それが有用であるそれらのまれな状況で、素晴らしい機能です。(いくつかのレベルから脱却するための提案を見てきましたが、それらは私に、発生するのを待っているバグのように見えます。)
David Thornley

それはJavaだったに違いない。回答ありがとうございます。そして、もちろん、あなたも残りの部分で正しいです。
Andrei Vajna II

-3
  while(true)
  {
    switch(x)
    {
     case 1:
     {
      break;
     }
    break;
   case 2:
    //some code here
   break;
  default:
  //some code here
  }
}

1
そのコードサンプルのすべてのブレークは、switchステートメントから抜け出します。
Dan Hulme
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.