forループでbreakを使用することは悪い習慣ですか?[閉まっている]


123

ループ内でbreakステートメントを使用することは悪い習慣ですか?for

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

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

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

PS:MISRAコーディングガイドラインでは、breakの使用は推奨されていません


28
:) breakと同じリーグに入れないでくださいgoto
BoltClock

2
ええ、私はそれらを同じリーグに配置していません...私が正しく覚えていれば、MISRAルールはブレークを指定しています。
kiki

1
私がループ内でブレークを使用しないと考えることができる唯一の理由は、あなたがまだ何をしているの結果を変えるかもしれないより多くのアイテムを処理しなければならないときです
Younes

4
MISRAルールが緩和されました:misra.org.uk/forum/viewtopic.php?f
Johan Kotlinski

回答:


122

ここにはたくさんの答えがありますが、これはまだ言及されていません。

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

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


11
かなり本当です。もちろん、ループが非常に大きく複雑で、ループ内で何が起こっているのかを確認するのが難しい場合、中断があるかどうかに関係なく問題になります。
ジェイ

1
別の問題は、中断して続行するとリファクタリングで問題が発生することです。これは、これが悪い習慣であることを示している可能性があります。ifステートメントを使用すると、コードの目的も明確になります。
Ed Greaves、2015年

これらの「適切な名前の関数呼び出し」は、他で使用されない外部定義のプライベート関数ではなく、ローカル定義のラムダ関数にすることができます。
DavidRR

135

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

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


2
同意した。特に、1つ以上の条件でループを終了しようとしている場合。ループを終了するためのブール値は、非常に混乱する可能性があります。
Rontologist

2
私が使用している理由ですgotoがないので-レベル2+ネストされたループから抜け出すためにbreak(level)
ПетърПетров

3
ループコードを関数に入れて、戻ります。これにより、gotoが回避され、コードが小さなチャンクに分割されます。
Dave Branton 2017年

53

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


47

使用breakなどcontinueforループすることは完全に罰金です。

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


はい。しばらくの間、このアイデアが気に入らなかったためコーディングしましたが、「回避策」がメンテナンスの悪夢であることに気づくまでに、それほど時間はかかりませんでした。まあ、悪夢ではありませんが、エラーが発生しやすくなります。
MetalMikester 2010年

24

ファー悪い習慣、パイソン(および他の言語?)から延長forループ、それの一部がされますので、構造をのみ、ループがあれば実行できません 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.')

2
ああ、私はそれが好きで、Cでもそれを望んだことがあります。ニースの構文も、曖昧さなく将来のCのような言語に適合させることができます。
スーパーキャット

「Cのような言語」:P
nirvanaswap

15

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)ステートメントは循環的複雑度を増加せ、コードがすべてのケースで正しいことを実行していることを証明することが困難になります。

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


4
循環的複雑度の+1。
sp00m 2013年

.NETプログラマーは、複雑なループの潜在的な代替手段としてLINQの使用を検討する必要があるかもしれませんfor
DavidRR

14

breakは完全に許容できるステートメントです(continue、btw 同様です)。それはすべてコードの読みやすさに関するものです。ループが複雑になりすぎていない限り、問題ありません。

gotoと同じリーグだったわけではありません。:)


私は、breakステートメント事実上gotoであると主張しているのを見ました。
glenatron

3
gotoの問題は、どこにでもポイントできることです。どちらも、コードのモジュール性を維持します。その引数は、各関数に単一の出口点があるのと同じレベルです。
ザカリーイェーツ

1
関数に複数の出口点があること実質的にgotoであると主張したと聞きました。; D
glenatron

2
@glenatron gotoの問題はgotoステートメントではなく、gotoがジャンプするのはラベルの導入です。gotoステートメントを読んだら、制御フローに不確実性はありません。ラベルを読むとき、制御フローについて多くの不確実性があります(このラベルに制御を移すすべてのgotoはどこにありますか?)。breakステートメントはラベルを導入しないので、新しい複雑化を導入しません。
Stephen C. Steel

1
「ブレイク」はブロックのみを残すことができる特別なgotoです。「goto」の歴史的な問題は次のとおりです。(1)これは、適切な制御構造では不可能な方法でオーバーラップするループブロックを構築するために使用できた可能性があり、しばしば使用されました。(2)通常「false」である「if-then」構文を実行する一般的な方法は、trueケースをコード内の無関係な場所に分岐させ、次に分岐させることでした。これは、まれなケースでは2つのブランチ、一般的なケースでは0のコストがかかりますが、コードが見苦しく見えます。
スーパーキャット

14

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

何かが見つかるまでループする場合、出てきたときに見つかるか見当たらないかを区別する問題に遭遇します。あれは:

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回だけ記述されます。


1
まさに私が言いたかったこと。多くの場合、自分がを使用breakしていることに気づいたとき、return代わりに使用できるように、コードを関数にリファクタリングする方法を見つけようとします。
Rene Saarsoo、

私はあなたと100%同意します。breakを使用することに本質的に問題はありません。ただし、通常は、コードが含まれているコードを、breakをreturnに置き換える関数に抽出する必要がある場合があることを示しています。それは完全な間違いではなく、「コードのにおい」と見なされるべきです。
vascowhite 2013

あなたの答えは、複数のreturnステートメントを使用することの妥当性を調査するように促すかもしれません。
DavidRR

13

言語によって異なります。ここでブール変数をチェックすることができますが:

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

配列を反復するときにそれを行うことはできません:

for element in bigList: ...

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


7

あなたの例では、forループの反復回数がわかりません。代わりにwhileループを使用ないでください。これにより、反復回数を最初は不確定にすることができますか?

したがって、ループはwhileループとしてより適切に記述できるため、一般的にはブレークステートメントを使用する必要はありません。


1
「for」ループは、既知の最大反復数がある場合に適しています。そうは言っても、while(1)ループは、アイテムを検索していて、アイテムが存在しない場合は作成する計画の場合に適していることがあります。その場合、コードがアイテムを見つけると直接ブレークし、配列の最後に到達すると新しいアイテムを作成してからブレークします。
スーパーキャット

2
与えられた例では、配列を検索してキーを探すために、forループが適切な慣用構文です。配列をウォークスルーするためにwhileループを使用しません。forループを使用します。(または、可能な場合はfor-eachループ)。「for(int i = 0; i <ra.length; i ++){}」構造を「Walk through the array」としてすぐに認識するため、これを他のプログラマへの礼儀として行います。単に「できれば早く終了する」という文です。
Chris Cudmore、

7

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

  • ブロックの上部で取得されたリソースは下部で解放される可能性があります(これはforループ内のブロックについても当てはまります)がbreak、「最新」のステートメントによって「時期尚早」の終了が発生した場合、その解放手順が誤ってスキップされる可能性がありますC ++、「RAII」を使用して、これを信頼性の高い例外安全な方法で処理します。基本的に、オブジェクトデストラクタは、スコープの終了方法に関係なく、リソースを確実に解放します。

  • for他の非ローカライズされた終了条件があることに気づかずに、ステートメントの条件テストを誰かが変更する可能性がある

  • ndimの答えは、一部の人々がbreaksを避けて比較的一貫性のあるループランタイムを維持する可能性があることを示していますが、それをbreak保持していないブール初期出口制御変数の使用と比較していました

そのようなバグを観察している人は時々、この「中断なし」ルールによってそれらを防止/軽減できることに気づきます...実際、「構造化プログラミング」と呼ばれる「より安全な」プログラミングには、各関数が持っているはずの関連する戦略全体があります。単一の入口および出口点(つまり、gotoなし、早期戻りなし)。いくつかのバグを取り除くかもしれませんが、間違いなく他のバグを紹介します。なぜ彼らはそれをするのですか?

  • 特定のスタイルのプログラミング/コードを奨励する開発フレームワークがあり、これがその限られたフレームワークで正味の利益を生み出すという統計的証拠がある、または
  • 彼らはプログラミングのガイドラインやそのようなフレームワーク内での経験に影響されてきた、または
  • 彼らは独裁的なバカです
  • 上記のいずれか+歴史的な慣性(正当化が最新のC ++よりもCに適用されやすいという点で重要)。

ええ、そうですが、もう一度、宗教的に避けようとした人々によるコードのバグを見てきましたbreak。はそれを避けたが、休憩の使用に取って代わる条件を熟考することに失敗した。
トマーシュZato -復活モニカ

5

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

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


5

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

breakはそれらの22のステートメントだけを見つけました

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

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

continue文も検索しました。見つかりませんでした。



4

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

    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;

これは非常に一般的な例であり、カーテンの後ろで多くのことが起こっています。特に割り込みです。これを定型コードとして使用しないでください。単に例を示しているだけです。

私の個人的な見解では、この方法でループを書くことは、無期限にループにとどまらないように適切な注意が払われている限り、何も問題はないということです。


1
詳しく説明してもらえますか?ループはまったく何もしないかのように見えます... continueアプリケーションロジック内にステートメントがない限り。ある?
Rene Saarsoo

1
無限ループに既に拘束されているため、continueステートメントは不要です。基本的に、目標を達成するために必要な必要なロジックを実行し、その目標がn回の試行で満たされない場合、またはタイムアウト条件が満了した場合は、break構文を介してループを終了し、修正アクションを実行するか、呼び出しコードにエラーを通知します。このタイプのコードは、共通バス(RS-485など)を介して通信するuCでよく見られます。基本的に、回線を捕捉して衝突なしで通信しようとします。衝突が発生した場合は、乱数で決定される期間待機してから、再び。
2010年

1
逆に、問題がなければ、breakステートメントを使用して、条件が満たされた後に終了します。この場合、ループに続くコードが実行され、全員が満足します。
2010年

「if(call_some_bool_returning_function())はTRUEを返し、それ以外はFALSEを返します。」単に「return call_some_bool_returning_function()」とすることもできます
フランクスター

3

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

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

ランタイムを気にしない場合はbreak;、およびcontinue;を使用して、コードを読みやすくします。


1
タイミングが重要な場合は、プログラムが無用な既知の操作を実行するよりも、ある程度のスリープを実行してシステムリソースを節約する方がよい場合があります。
Bgsは2013年

2

MISRA C devの中で私の会社で使用されている98のルール、break文を使用してはなりません...

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


2
まさに私がこの質問をした理由!そのようなルールがコーディングガイドラインとして存在する理由はわかりません。また、他の皆が言ったように、休憩してください。良く見えます。
kiki

1
なぜそれが禁じられているのか正確にはわかりません(私が考えているよりも賢い人です...)が、読みやすさのために改行(および複数の改行)の方が優れています(私の意見では、私の意見は企業規則ではありません... )
2010年

1
ええと、MISRAルールでは、breakステートメントの使用が許可されています:misra.org.uk/forum/viewtopic.php?f
75&

私たちは、それはMISRA 2004ルールを変更することを正確である... MISRA 98個のルールを使用
ブノワ・

0

もちろん、break;forループまたはforeachループを停止するソリューションです。私はそれをphpのforeachとforループで使用し、動作することを発見しました。


0

同意しません!

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]);
}

改ざんされた「Professional Code」は、プロのコードのようには聞こえません。それは遅延コーディングのように聞こえます;)


4
レイジーコーディングはプロフェッショナルコードです:)
Jason

それを維持するのがお尻の痛みではない場合にのみ:)
Biff MaGriff

2
私は「GTFO ASAP」を練習する傾向があります。つまり、構造体をきれいに終了できる場合は、できるだけ早く、終了を指定する条件に近い状態で終了する必要があります。
Chris Cudmore、

まあそれも動作します。私が理解しようとしているのは、forループを使用している場合でも、組み込みの機能を使用してコードを読みやすくモジュール化するのに支障がないことです。ループ内に絶対にbreakステートメントが必要な場合、私は座って、そのレベルの複雑さが必要な理由について考えます。
Biff MaGriff

1
はは。breakコードを読みやすくするためであると反対するほとんどの人が主張するという事実を考慮してください。今、上記のループでクレイジー5マイル長い条件で再び見て...
トマーシュZato -復活モニカ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.