web-dev-qa-db-ja.com

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

私はこのようなコードを書いています:

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

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

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

104
jrharshath

前提

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

_while( true ) {
}
_

サポート引数

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

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

「移動」の代替

次のコードのほうが適切な形式です。

_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. ユーザーの選択がWordの「再起動」である間にループします。
  3. ユーザーに選択を入力してもらいます。
  4. 終わり。
_choice = 'restart';

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

print 'Break!'
_

ここで、_while True_は、誤解を招くような過度に複雑なコードになります。

50
Dave Jarvis

gotoを使用できます。

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

   }
}
exit_loop: ;
152
Mehrdad Afshari

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

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

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

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

52
sakra

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

int yourfunc() {

    while(true) {

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

    }
}

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

20
Alterlife

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

14
Amber

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

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())
    ;
8
Adam Pierce

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

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

this answerのようにgotoを使用します。この場合、gotoは他のオプションよりもコードを明確にします。 this の質問が役立つことを願っています。

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

_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 ...
}
_

この場合、ループから抜け出すためのC++コンストラクトはありません。

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

5
sharptooth

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

2
Martin York

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

do { 
    switch(option){
        case 1: ..; break;
        ...
        case n: .. ;break;
        default: flag = false; break;
    }
} while(flag);
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
Mark B

おもう;

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

説明の深さを考えると、これがどれほど簡単か驚かされます...必要なものはすべてここにあります...

bool imLoopin = true;

while(imLoopin) {

    switch(msg->state) {

        case MSGTYPE: // ... 
            break;

        // ... more stuff ...

        case DONE:
            imLoopin = false;
            break;

    }

}

笑!!本当に!必要なのはそれだけです! 1つの追加変数!

0
MarqueIV

最も簡単な方法は、SWITCHを実行する前に単純なIFを配置し、そのIFがループを終了するための条件をテストすることです。

0
SpiriAd

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

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

    //...
    }
}

msg->stateDONEと等しく(クリーンアップルーチンを実行するなど)、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
Daniel Trebbien
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;
}
0
Ram Suthar