web-dev-qa-db-ja.com

forループでbreakを使用するのは悪い習慣ですか?

break statementforループ

たとえば、配列の値を検索しています。 forループ内で比較し、値が見つかったらbreak;を使用してforループを終了します。

これは悪い習慣ですか?代替が使用されているのを見ました:変数vFoundを定義し、値が見つかったらtrueに設定し、vFoundステートメント条件でforを確認します。しかし、この目的のためだけに新しい変数を作成する必要がありますか?

通常のCまたはC++ forループのコンテキストで尋ねています。

追伸: MISRAコーディングガイドライン は、breakの使用を推奨しません。

115
kiki

ここにはたくさんの答えがありますが、私はまだこれが言及されているのを見ていません:

整然とした読みやすいループを記述すると、forループでbreakまたはcontinueを使用することに関連する「危険」のほとんどが無効になります。ループの本体が複数の画面長にまたがり、複数のネストされたサブブロックがある場合、はい、ブレーク後に一部のコードが実行されないことを簡単に忘れることができます。ただし、ループが短くて要点を示している場合、breakステートメントの目的は明らかです。

ループが大きくなりすぎている場合は、代わりにループ内で1つ以上の適切な名前の関数呼び出しを使用します。これを避ける唯一の本当の理由は、処理のボトルネックのためです。

107
e.James

いいえ、ブレークは正しい解決策です。

ブール変数を追加すると、コードが読みにくくなり、潜在的なエラーの原因が追加されます。

132
smirkingman

「break」ステートメントを含むあらゆる種類のプロフェッショナルコードを見つけることができます。必要なときにこれを使用することは完全に理にかなっています。あなたの場合、このオプションは、ループから抜け出すためだけに別の変数を作成するよりも優れています。

50
Amit S

breakループでcontinueと同様にforを使用することはまったく問題ありません。

コードを簡素化し、読みやすさを向上させます。

46
user151323

悪い習慣からほど遠い、Python(および他の言語?)は forループ 構造を拡張し、その一部がonlyループが実行されない場合breakに実行されます。

for n in range(5):
    for m in range(3):
        if m >= n:
            print('stop!')
            break
        print(m, end=' ')
    else:
        print('finished.')

出力:

stop!
0 stop!
0 1 stop!
0 1 2 finished.
0 1 2 finished.

breakおよびその便利なelseなしの同等のコード:

for n in range(5):
    aborted = False
    for m in range(3):
        if not aborted:
            if m >= n:
                print('stop!')
                aborted = True
            else:            
                print(m, end=' ')
    if not aborted:
        print('finished.')
21
Nick T

一般的なルール:ルールに従うために、より厄介で読みにくい操作を行う必要がある場合は、ルールを破ってからルールを破ってください。

何かを見つけるまでループする場合、外に出たときに、見つかったものと見つからなかったものを区別する問題に遭遇します。あれは:

for (int x=0;x<fooCount;++x)
{
  Foo foo=getFooSomehow(x);
  if (foo.bar==42)
    break;
}
// So when we get here, did we find one, or did we fall out the bottom?

したがって、フラグを設定するか、「found」値をnullに初期化できます。しかし

そのため、一般に検索を関数にプッシュすることを好みます。

Foo findFoo(int wantBar)
{
  for (int x=0;x<fooCount;++x)
  {
    Foo foo=getFooSomehow(x);
    if (foo.bar==wantBar)
      return foo;
  }
  // Not found
  return null;
}

これは、コードを整理するのにも役立ちます。メイン行では、「find」は単一のステートメントになり、条件が複雑な場合は1回だけ記述されます。

14
Jay

Breakステートメントの使用に本質的に問題はありませんが、ネストされたループは混乱を招く可能性があります。読みやすさを改善するために、多くの言語( 少なくともJavaはあります )は、ラベルの区切りをサポートし、読みやすさを大幅に改善します。

int[] iArray = new int[]{0,1,2,3,4,5,6,7,8,9};
int[] jArray = new int[]{0,1,2,3,4,5,6,7,8,9};

// label for i loop
iLoop: for (int i = 0; i < iArray.length; i++) {

    // label for j loop
    jLoop: for (int j = 0; j < jArray.length; j++) {

        if(iArray[i] < jArray[j]){
            // break i and j loops
            break iLoop;
        } else if (iArray[i] > jArray[j]){  
            // breaks only j loop
            break jLoop;
        } else {
            // unclear which loop is ending
            // (breaks only the j loop)
            break;
        }
    }
}

Break(およびreturn)ステートメントは、多くの場合増加します 循環的複雑度 これは、すべての場合においてコードが正しいことをしていることを証明するのを難しくします。

特定のアイテムのシーケンスを反復処理するときにブレークを使用することを検討している場合は、データを保持するために使用されるデータ構造を再検討する必要があります。セットやマップなどを使用すると、より良い結果が得られる場合があります。

14
cyber-monk

breakは完全に受け入れられるステートメントです(continue、btwも同様です)。それはすべてコードの可読性に関するものです-複雑すぎるループなどがなければ、問題ありません。

gotoと同じリーグではなかった。 :)

13
riviera

言語に依存します。ここでブール変数をチェックすることもできますが:

for (int i = 0; i < 100 && stayInLoop; i++) { ... }

配列を繰り返し処理する場合、それを行うことはできません。

for element in bigList: ...

とにかく、breakは両方のコードを読みやすくします。

13
eumiro

breakの使用を推奨する他の人に同意します。明らかな結果的な質問は、なぜ誰かがそうでなければお勧めするのでしょうか?さて... breakを使用すると、ブロック内の残りのコードと残りの反復をスキップします。時々これはバグを引き起こします、例えば:

  • ブロックの上部で取得したリソースは下部で解放される場合があります(これはforループ内のブロックについても同様です)が、「早期」終了がbreakステートメント(「モダン」C++、「RAII」は、これを信頼性の高い例外安全な方法で処理するために使用されます。基本的に、オブジェクトデストラクターは、スコープがどのように終了しても、リソースを確実に解放します)

  • 誰かが、forステートメントの条件付きテストを、他の非ローカライズされた終了条件があることに気付かずに変更する場合があります

  • ndimの答えは、一部の人々はbreaksを避けて比較的一貫したループ実行時間を維持するかもしれないが、breakをブール値の早期終了制御変数の使用と比較していた

そのようなバグを観察する人々は、この「ブレークなし」のルールによって防止/緩和できることに気づきます...実際、「構造化プログラミング」と呼ばれる「安全な」プログラミングには、各関数が持つはずの全体的な関連戦略があります入り口と出口も1つだけです(つまり、gotoなし、早期復帰なし)。それはいくつかのバグを排除するかもしれませんが、間違いなく他のものを導入します。なぜ彼らはそれをするのですか?

  • 特定のスタイルのプログラミング/コードを奨励する開発フレームワークがあり、その限られたフレームワークでこれが純利益をもたらすという統計的証拠、または
  • そのようなフレームワーク内のプログラミングガイドラインまたは経験の影響を受けている、または
  • 彼らはただ独裁的なばかです、または
  • 上記+歴史的慣性のいずれか(最新のC++よりも正当化の方がCにより適切であるという意味で)。
7
Tony Delroy

この例では、forループの反復回数はわかりません。代わりにwhileループを使用しないのはなぜですか?

したがって、一般的にbreak statemementを使用する必要はありません。ループはwhileループとしてより適切に記述できるためです。

7
Schedler

breakを使用することは完全に有効です。他の人が指摘しているように、gotoと同じリーグにはありません。

値が配列内で見つかったかどうかをループ外で確認する場合は、vFound変数を使用することもできます。保守性の観点からも、終了基準を通知する共通のフラグを使用すると便利です。

5
Ace

その時点でSTOP処理を完了するのが悪い習慣になる理由はわかりません。

組み込みの世界では、次の構成を使用する多くのコードがあります。

    while(1)
    { 
         if (RCIF)
           gx();
         if (command_received == command_we_are_waiting_on)
           break;
         else if ((num_attempts > MAX_ATTEMPTS) || (TickGet() - BaseTick > MAX_TIMEOUT))
           return ERROR;
         num_attempts++;
    }
    if (call_some_bool_returning_function())
      return TRUE;
    else
      return FALSE;

これは非常に一般的な例であり、多くのことがカーテンの後ろで、特に割り込みが発生しています。これを定型的なコードとして使用しないでください。私は単に例を説明しようとしています。

私の個人的な意見では、ループ内に無期限に残ることを防ぐために適切な注意を払う限り、この方法でループを書くことは何の問題もないと考えています。

4
Nate

現在取り組んでいるコードベース(40,000行のJavaScript)について分析を行いました。

22個のbreakステートメントのみが見つかりましたそれらの:

  • 19個がswitchステートメント内で使用されました(合計で3つのswitchステートメントしかありません!)。
  • 2はforループ内で使用されました-すぐに別の関数にリファクタリングされると分類し、returnステートメントに置き換えられたコード。
  • 最後のbreak内部whileループについては... git blameを実行して、このがらくたを書いた人を確認しました!

私の統計によると:breakswitchの外で使用されている場合、それはコードの匂いです。

continueステートメントも検索しました。見つかりません。

4
Rene Saarsoo

ユースケースに依存します。 forループの実行時間を一定にする必要があるアプリケーションがあります(たとえば、いくつかのタイミング制約を満たすため、またはタイミングベースの攻撃からデータ内部を隠すため)。

これらの場合、フラグを設定し、すべてのforループ反復が実際に実行された後にのみフラグ値をチェックすることも理にかなっています。もちろん、すべてのforループの反復では、ほぼ同じ時間がかかるコードを実行する必要があります。

実行時を気にしない場合... break;およびcontinue;を使用して、コードを読みやすくします。

3
ndim

MISRA 98ルールでは、C devで私の会社で使用されていますが、breakステートメントは使用されません...

編集:MISRA '04ではブレークが許可されています

2
Benoît

同意しません!

独自のforループの組み込み機能を無視するのはなぜですか?車輪を再発明する必要はありません。

Forループの一番上にチェックを入れておくのがより理にかなっていると思います

for(int i = 0; i < myCollection.Length && myCollection[i].SomeValue != "Break Condition"; i++)
{
//loop body
}

または最初に行を処理する必要がある場合

for(int i = 0; i < myCollection.Length && (i == 0 ? true : myCollection[i-1].SomeValue != "Break Condition"); i++)
{
//loop body
}

これにより、すべてを実行し、よりクリーンなコードを作成する関数を作成できます。

for(int i = 0; i < myCollection.Length && (i == 0 ? true : myCollection[i-1].SomeValue != "Break Condition"); i++)
{
    DoAllThatCrazyStuff(myCollection[i]);
}

または、条件が複雑な場合は、そのコードも外に移動できます!

for(int i = 0; i < myCollection.Length && BreakFunctionCheck(i, myCollection); i++)
{
    DoAllThatCrazyStuff(myCollection[i]);
}

ブレークに満ちた「プロフェッショナルコード」は、私にはプロフェッショナルコードのようには聞こえません。怠codingなコーディングのようですね;)

1
Biff MaGriff

もちろん、break;はforループまたはforeachループを停止するソリューションです。 foreachおよびforループのphpで使用し、動作していることがわかりました。

0
gautamlakum