7

終了条件 (正しい反復回数に達したこととは異なる) が検証されたときに、C++ で for ループを終了できるかどうかを知りたいです。例えば:

for (int i = 0; i < maxi; ++i)
    for (int j = 0; j < maxj; ++j)
        // But if i == 4 < maxi AND j == 3 < maxj, 
        // then jump out of the two nested loops.

次の LABEL または最後の LABEL 呼び出しとラベル付きブロックを使用して Perl でこれが可能であることは知っていますが、C++ でそれを行うことは可能ですか、それとも while ループを使用する必要がありますか?

ありがとうございました。

4

15 に答える 15

51

キーワードを使用できますreturn: ネストされたループをサブルーチンに移動し、サブルーチンを呼び出してネストされたループを実行し、サブルーチンから「戻る」ことで [すべて] ループを終了します。

于 2009-01-06T13:33:03.527 に答える
47

goto「有害と見なされる」という議論にもかかわらず、これはgoto. これは基本的に、Perl で行っていることです。真剣に...代替案を検討してください:

追加の状態変数


for (int i=0; i<maxi; ++i) {
    bool leaveLoop = false;
    for (int j=0; j<maxj; ++j) {
        if (i == 4 && j == 3) {
            leaveLoop = true;
            break; // leave the inner loop
        }
    }
    if (leaveLoop) {
        break; // leave the outside loop
    }
}

例外的に休暇


try {
    for (int i=0; i<maxi; ++i) {
        for (int j=0; j<maxj; ++j) {
            if (i == 4 && j == 3) {
                throw leave_loop();
            }
        }
    }
} catch (leave_loop const&) {
}

複雑なロジック


int j = 0;
for (int i=0; i<maxi && !(i==4 && j==3); ++i) {
    for (j=0; j<maxj && !(i==4 && j==3); ++j) {
        // inner loop
    }
}

goto


for (int i=0; i<maxi; ++i) {
    for (int j=0; j<maxj; ++j) {
        if (i==4 && j==3) {
            goto leave_loop;
        }
    }
}
leave_loop:

最後の方がわかりにくい?私はそうは思いません。これ以上壊れやすいですか?IMHO、他のバージョンは、バージョンに比べて非常にエラーが発生しやすく、壊れやすいgotoです。ここで石鹸箱に立って申し訳ありませんが、これはしばらく私を悩ませていたものです ;)

知っておく必要がある唯一のことは、gotoと 例外はかなり似ているということです。どちらもリソースをリークする可能性があり、そうでないものは慎重に扱ってください。

于 2009-01-06T13:45:19.213 に答える
13

できる限り強調して (しかし丁寧に ;-) 言わせてくださいfor

続行するかどうかを決定するテスト式は、ループの目的に関連するものであれば何でもかまいません。更新式は「カウンターに 1 を追加する」である必要はありません。

for (int i = 0, j = 0; i < maxi && j < maxj && i != 4 && j != 3;) {
    if (j < maxj) {
        ++j;
    } else {
        j = 0;
        ++i;
    }
}

書き換える(かなり恣意的な)方法の1つです。

要点は、何らかの条件を確立することが反復のポイントである場合、継続/終了条件をより明示的に示す方法で(whileまたは のいずれかを使用して) ループを記述することが通常可能であるということです。for

(実際に何が起こっているかの説明を投稿できれば、上記のように恣意的に見えないものを書く可能性があります。)

于 2009-01-06T13:31:56.340 に答える
8

1 つの break 命令で2 つのループから飛び出すことはできませんが、goto を使用して内側のループからすぐ外側に飛び出すことができます。

goto がローカライズされていて、そうでない場合よりもロジックが少ないことを意味する場合、それは完全に受け入れられるコードだと思います。追加のフラグ変数を用意したり、反復子変数を内側のループから持ち上げたりして、外側のループで比較できるようにしても、コード IMHO を理解しやすくすることはできません。

于 2009-01-06T13:22:23.580 に答える
8

上記のすべての提案から、例外は通常の制御フローではなく、例外的な状況のために予約する必要があるため、try/catch メカニズムの使用は避けます。

2 つ目の条件を適切に作成できれば、2 つのブレークを使用しても問題ありません。この目的でブール値を使用するのも良いでしょう。それを各 for ループの条件に配線することもできます。例えば:

bool exit_loops = false;
for (int a = 0; a < A && !exit_loops; ++a)
{
    for (int b = 0; b < B && !exit_loops; ++b)
    {
        if (some_condition) exit_loops = true;
    }
}

ただし、2 つ以上のループを使用している場合は、それらを関数でラップし、return を使用して関数 (およびすべてのループ) を終了する方が適切な場合があります。次に、内部ループ コードを実行する関数を呼び出すなどして、1 つを除くすべてのループを排除できる方法でコードをリファクタリングすることができます。

最後に、この状況で goto を使用することを恐れないでください。通常、goto は不適切な非構造化プログラミングですが、(このような) 場合によっては非常に便利です。

于 2009-01-06T13:48:07.367 に答える
6
bool done = false;

for (int i = 0; i < maxi && !done; ++i)
    for (int j = 0; j < maxj && !done; ++j)
        if (i == 4 && i < maxi && j == 3 && j < maxj )
             done = true;
        else {
        }

または、ただ行くこともできます。か否か :-)

于 2009-01-06T13:47:34.450 に答える
6

C/C++ では次のように飛び出すことはできません。

for (...)
{
  for (...)
  {
    // from here...
  }
}
// ...to here

goto を使用せずに。次のような構成が必要です。

for (...)
{
  bool
    exit = false;

  for (...)
  {
    if (do_exit)
    {
      exit = true; // or set outer loop counter to end value
      break;
    }
  }
  if (exit)
  {
    break;
  }
}

別の方法として、throw と catch を使用します。ただし、throw は実際にはフロー制御ではなく例外に使用する必要があるため、これは適切ではありません。

クリーンな方法は、内側のループを関数にすることです。

bool F ()
{
  if inner loop terminates, return false else return true
}

void G ()
{
  for (...)
  {
    if (!F ())
    {
      break;
    }
  }
}
于 2009-01-06T13:25:17.287 に答える
4
for (int i = 0; i < maxi; ++i)
{
    int j = 0;
    for (j = 0; j < maxj; ++j)
    {
         if (i == 4 && j == 3) // i < maxi and j < maxj otherwise we would not be here
             break; // exit inner loop
    }
    if (i == 4 && j == 3) // i < maxi and j < maxj otherwise we would not be here
        break; // exit outer loop
}
于 2009-01-06T13:22:36.543 に答える
4

goto ステートメントを使用することもできますが、それは一般的に悪い習慣と考えられています。

あなたの他のオプションは、このようなことをすることです

int i;
int j = 0;
for (i = 0; i < maxi && !(i==4 && j==3); ++i)
    for (j = 0; j < maxj && !(i==4 && j==3); ++j)
于 2009-01-06T13:22:58.837 に答える
2

コードを読むことは、探偵の本を読むようなものであってはなりません(常に理解する必要があります)...

例:

Java:

iterate_rows:
for (int i = 0; i < maxi; ++i)
{       
    for (int j = 0; j < maxj; ++j)
    {
        if (i == 4 < maxi && j == 3 < maxj) 
            break iterate_rows;
        else
            continue iterate_rows;
    }   
}

break iterate_rowsが何をするのかを理解する必要はなく、読むだけです。

C ++:

//iterate_rows:
for (int i = 0; i < maxi; ++i)
{
    for (int j = 0; j < maxj; ++j)
    {
        if (i == 4 < maxi && j == 3 < maxj) 
            goto break_iterate_rows;
        else
            goto continue_iterate_rows;
    }

continue_iterate_rows:;
}
break_iterate_rows:;

goto break_iterate_rowsは、breakiterate_rowsの表示バージョンです

gotoとラベルの使用をこの種のコードのみに限定すると、意図を理解できなくなります。この種のコードでgotoとラベルの使用を制限すると、コードを分析したり理解したりするのではなく、コードを読むだけになります。あなたは邪悪なプログラマーであると非難されることはありません。

そして、この種のコードでgotoを本当に制限すれば、それらのdarngotosがコードで何をするのかを理解する必要がないという習慣を身につけることができます。追加の利点は、ブール値を導入して追跡する必要がないことです(これにより、コードが検出され、少し読みにくくなり、gotoを回避するという目的そのものが無効になります)

PS

それらのラベルをコメント(ループの前)とペアにします。gotoステートメントでこれらの行を過ぎて読むまでに、それらのgotoの意図をすでに知っています。

于 2009-01-06T15:11:43.470 に答える
1

構成を完全に再検討するもう1つの理由はfor、ループが終了した後、そのスコープによって制御変数へのアクセスが妨げられるためです。ループ内で変更される変数の値は、さまざまな理由で役立つ場合があります(たとえば、検索の成功と失敗を区別するため)。そうでない場合は、スコープの終了後にその情報を保持するために追加の変数が必要になります。aこれは、target値の名前が付けられた正方形の配列を検索する小さな例です(SIZEゼロ以外の場合、検索は必要ありません!):

int i = 0;
int j = 0;
while (i < SIZE && a[i][j] != target) { // still in array but not at target
    if (SIZE <= ++j) {                  // fallen off the end of a row
        j = 0;
        ++i;
    }
}

後続のコードを使用i < SIZEして、目的の値が見つかったかどうかを判断できます。

上記のもう1つの利点は、柔軟性です。の行の値が昇順であることが通知されたとします。aしたがって、より大きい値が検出された場合、行の残りの部分は関係ありませんtarget。どのような変更を加え、どこで変更するかを正確に知るのは簡単です。その新しい情報により、現在の行を破棄できるため、内部の決定のみが影響を受け、次のようになります。

    if (target < a[i][j] || SIZE <= ++j) { // can't be in row or fallen off end
    ...

古い「カウント」ループ構造を放棄する新しい言語(特に機能指向の言語)が増えています。単に数えるのではなく、ループの意味について考えるようになるので、それはおそらく良いことです。

于 2009-01-06T18:36:58.877 に答える
1

私はいつもgoto声明を避けるように努めてきました(何らかの理由で学校や私の仕事でいつも見下されていました). Daeminが提案したようなものを使用します。

于 2009-01-06T14:02:59.507 に答える
1

次のようなラベルを使用できます。

Outer:
for(...)
{
    Inner:
    for(...)
    {
    if(condition)
        {
        goto End;
        }
    }
}
End:

Javaでは、ラベルを渡してブレークできますか?

編集 - goto を Outer ではなく End に変更しましたが、否定的な表現が正当化されるとは思いません。この答えは、それを行う最も簡単な方法を提供します。

于 2009-01-06T13:20:53.663 に答える
1

いくつかの提案があります:

  1. throw .... 2 つのループを「try {}」内に配置し、条件で「throw」を「キャッチ」します。

  2. メソッドに 2 つのループを配置し、条件に戻ります。

  3. Goto は悪ではありません。人々がそれを使用することも.... "goto" を使用できます。特にエラーを処理する場合は、最も明確なコードになる可能性があります。もう20年使ってません。

トニー

于 2009-01-06T13:31:44.310 に答える