74

私はよく次のコード パターンを使用します。

while(true) {

    //do something

    if(<some condition>) {
        break;
    }

}   

別のプログラマーは、これは悪い習慣であり、より標準的なものに置き換える必要があると私に言いました:

while(!<some condition>) {

    //do something

}   

彼の推論は、「休憩を忘れる」ことがあまりにも簡単で、無限ループが発生する可能性があるというものでした. 私は彼に、2 番目の例では、true を返さない条件を同じように簡単に設定できるので、同じように簡単に無限ループを作成できるので、どちらも同じように有効なプラクティスであると伝えました。

さらに、複数のブレークポイント、つまりループから抜け出す複数の条件がある場合にコードが読みやすくなるため、前者を好むことがよくあります。

どちらか一方の証拠を追加して、この議論を充実させることができる人はいますか?

4

22 に答える 22

58

2 つの例の間には矛盾があります。最初のステートメントは、ステートメントが決して真でなくても、毎回少なくとも 1 回は「何かをする」を実行します。2 番目は、ステートメントが true と評価された場合にのみ「何かを実行」します。

あなたが探しているのは do-while ループだと思います。while (true)このコードを維持するのが難しくなり、ループをエスケープする方法が非常にgoto風変わりであり、悪い習慣と見なされるため、それは良い考えではないことに100%同意します。

試す:

do {
  //do something
} while (!something);

正確な構文については、個々の言語のドキュメントを確認してください。しかし、このコードを見てください。基本的に do の内容を実行し、while 部分をチェックして、再度実行する必要があるかどうかを確認します。

于 2008-12-24T00:37:10.977 に答える
32

ワーズワースの著名な開発者の言葉を引用すると、

...
実際、私たちが自分自身を運命づけて
いる刑務所は、刑務所ではありません。したがって、私にとっては、さまざまな気分 で、ソネットのわずかな土地の中
に縛られるのが娯楽でした。 私が見つけたように、あまりにも多くの自由の重さを感じた 魂が(そのような必要があるに違いない) そこに短い慰めを見つけることができれば幸いです.



ワーズワースは、ソネットの厳格な要件を拘束具としてではなく、解放の枠組みとして受け入れました。「構造化プログラミング」の核心は、複雑なフロー グラフを任意に作成する自由を放棄して、解放的な理解の容易さを優先することにあると思います。

早期退出が行動を表現する最も簡単な方法である場合があることに、私は自由に同意します。しかし、私の経験では、可能な限り単純な制御構造を使用するように強制すると (そして、これらの制約内で設計することを真剣に考えます)、その結果、より単純で明確なコードになることがよくあります。との欠点

while (true) {
    action0;
    if (test0) break;
    action1;
}

つまり、コードのチャンクをどんどん大きくしたり、「もう 1 つだけ」テスト - ブレーク - アクションのシーケンスを追加したりするのは簡単で、特定の行を指して「どのような条件が必要か」という質問に答えるのが難しくなりaction0ます。action1この時点でホールドを知っていますか?」そのため、他のプログラマー向けのルールを作成することなくwhile (true) {...}、自分のコードでは可能な限りイディオムを避けるようにしています。

于 2008-12-24T02:19:46.557 に答える
25

あなたがフォームであなたのコードを書くことができるとき

while (condition) { ... }

また

while (!condition) { ... }

本文に出口(、、、または)がない場合breakcontinuegotoヘッダーを見るだけでコードを読み取って終了条件を理解できるため、この形式が推奨されます。それは良い。

しかし、多くのループはこのモデルに適合しません。中央に明示的な出口がある無限ループは、立派なモデルです。(ループcontinueは通常、ループよりも理解が困難です。)引用する証拠や権限が必要な場合は、Gotoステートメントを使用した構造化プログラミングbreakに関するDonKnuthの有名な論文をご覧ください。あなたが望むかもしれないすべての例、議論、そして説明を見つけるでしょう。

イディオムのマイナーなポイント:書くwhile (true) { ... }ことはあなたを古いPascalプログラマーまたはおそらく最近のJavaプログラマーとしてブランド化します。CまたはC++で記述している場合、推奨されるイディオムは次のとおりです。

for (;;) { ... }

これには正当な理由はありませんが、Cプログラマーが期待する方法であるため、このように記述する必要があります。

于 2008-12-24T01:21:31.340 に答える
13

私は好きです

while(!<some condition>) {
    //do something
}

しかし、それは「休憩を忘れる」可能性というよりも、読みやすさの問題だと思います。breakそれはバグであり、すぐに見つけて修正するので、それを忘れることはかなり弱い議論だと思います。

break無限ループから抜け出すためにaを使用することに反対する議論は、基本的にbreakステートメントを。として使用しているということですgoto。私はgoto(言語がそれをサポートしている場合、それは公正なゲームです)使用することに宗教的に反対していませんが、より読みやすい代替手段がある場合はそれを置き換えるようにしています。

多くのbreak点の場合、私はそれらを次のように置き換えます

while( !<some condition> ||
       !<some other condition> ||
       !<something completely different> ) {
    //do something
}

このようにすべての停止条件を統合すると、このループを終了する対象を簡単に確認できます。 breakステートメントが散らばっている可能性があり、それは読みやすいものではありません。

于 2008-12-24T00:49:37.130 に答える
11

while(true)は、ステートメントが多く、失敗した場合は停止したい場合に意味があります。

 while (true) {
     if (!function1() ) return;   
     if (!function2() ) return;   
     if (!function3() ) return;   
     if (!function4() ) return;  
   }

よりも良い

 while (!fail) {
     if (!fail) {
       fail = function1()
     }           
     if (!fail) {
       fail = function2()
     }  
     ........

   }
于 2008-12-24T01:17:24.017 に答える
7

ハビエルは、私の以前の回答 (ワーズワースを引用したもの) に対して興味深いコメントをしました。

while(true){} は while(condition){} よりも「純粋な」構造だと思います。

300文字で十分に答えられませんでした(ごめんなさい!)

私は教育とメンタリングにおいて、「複雑さ」を「この 1 行または式を理解できるようにするために頭の中に必要な残りのコードの量はどれくらいか」と非公式に定義しました。覚えておかなければならないことが多ければ多いほど、コードは複雑になります。コードが明示的に教えてくれるほど、複雑さが軽減されます。

したがって、複雑さを軽減するという目標を掲げて、純粋さではなく完全性と強さの観点からハビエルに返信させてください。

このコードフラグメントについて考えます:

while (c1) {
    // p1
    a1;
    // p2
    ...
    // pz
    az;
}

2 つのことを同時に表現する場合:

  1. c1(全体の) 本体はtrue である限り繰り返されます。
  2. a1が実行されるポイント 1 では、保持c1保証されます。

違いは視点の 1 つです。a1これらの最初のものは、一般的にループ全体の外側の動的な動作に関係していますが、2 つ目は、特に考えているときに信頼できる内側の静的な保証を理解するのに役立ちます。もちろん、 の正味の効果はa1を無効c1にする可能性があり、ポイント 2 で何を当てにできるかについてより真剣に考える必要があります。

条件と最初のアクションについて考えるために、特定の (小さな) 例を配置してみましょう。

while (index < length(someString)) {
    // p1
    char c = someString.charAt(index++);
    // p2
    ...
}

「外側」の問題は、ループが に配置さsomeStringれている限り実行できる何かを明らかに実行していることです。これにより、終了が最終的に発生するように、体のいずれかまたは内部 (場所と方法は、体を調べるまではわかりません) を変更するという期待が設定されます。それは、身体について考えるための文脈と期待の両方を私に与えてくれます。indexsomeStringindexsomeString

「内部」の問題は、ポイント 1 に続くアクションが正当であることが保証されていることです。そのため、ポイント 2 のコードを読みながら、合法的に取得されたことがわかっているchar 値で何が行われているかを考えることができます。(条件 if が null ref であることを評価することさえできませんsomeStringが、この例のコンテキストではそれを回避していると仮定しています!)

対照的に、次の形式のループ:

while (true) {
    // p1
    a1;
    // p2
    ...
}

両方の問題について私を失望させます。外側のレベルでは、このループが永遠に繰り返されることを本当に期待すべきなのか (オペレーティング システムのメイン イベント ディスパッチ ループなど)、それとも何か他のことが起こっているのか疑問に思っています。これは、本文を読むための明示的な文脈も、(不確実な) 終了に向けた進歩を構成するものについての期待も与えません。

内部レベルでは、ポイント 1 で成立する可能性のある状況について、明示的な保証はまったくありませんtrue。条件は、もちろんどこでも真ですが、プログラムの任意の時点で知ることができることに関する最も弱いステートメントです。アクションの前提条件を理解することは、アクションが何を達成するかを考えようとするときに非常に貴重な情報です!

したがって、このwhile (true) ...イディオムは、上で説明した論理よりもはるかに不完全で弱いため、より複雑であるとwhile (c1) ...思います。

于 2008-12-25T17:25:45.700 に答える
6

問題は、すべてのアルゴリズムが「while(cond){action}」モデルに固執しているわけではないことです。

一般的なループ モデルは次のようになります。

loop_prepare
 loop:
  action_A
  if(cond) exit_loop
  action_B
  goto loop
after_loop_code

action_A がない場合は、次のように置き換えることができます。

loop_prepare
  while(cond)
  action_B
after_loop_code

action_B がない場合は、次のように置き換えることができます。

loop_prepare
  do action_A
  while(cond)
after_loop_code

通常、action_A は n 回実行され、action_B は (n-1) 回実行されます。

実際の例は次のとおりです。コンマで区切られたテーブルのすべての要素を出力します。(n-1) 個のカンマを含む n 個の要素すべてが必要です。

while ループ モデルに固執するためにいつでもいくつかのトリックを行うことができますが、これは常にコードを繰り返すか、(ループごとに) 同じ条件を 2 回チェックするか、新しい変数を追加します。そのため、常に while-true-break ループ モデルよりも効率が悪く、読みにくくなります。

(悪い)「トリック」の例:変数と条件を追加する

loop_prepare
b=true // one more local variable : more complex code
 while(b): // one more condition on every loop : less efficient
  action_A
  if(cond) b=false // the real condition is here
  else action_B
after_loop_code

(悪い)「トリック」の例:コードを繰り返します。2 つのセクションのいずれかを変更するときに、繰り返されるコードを忘れてはなりません。

loop_prepare
action_A
 while(cond):
  action_B
  action_A
after_loop_code

注 : 最後の例では、プログラマーは "loop_prepare" を最初の "action_A" に、action_B を 2 番目の action_A に混ぜて、コードを難読化できます (意図的かどうかに関係なく)。そのため、彼は自分がこれを行っていないという感覚を持つことができます。

于 2014-05-05T09:52:02.203 に答える
4

ループから抜け出す方法がたくさんある場合、またはループの先頭でブレーク条件を簡単に表現できない場合 (たとえば、ループの内容は途中で実行する必要があるが、残りの半分は実行してはならない) の場合は、最初の方法で問題ありません。 、最後の反復で)。

しかし、それを避けることができるなら、避けるべきです。なぜなら、プログラミングは非常に複雑なものを可能な限り最も明白な方法で記述し、機能を正しく効率的に実装することであるべきだからです。そういうわけで、あなたの友人は一般的には正しいのです。あなたの友人のループ構造の書き方は、はるかに明白です (前の段落で説明した条件が得られないと仮定します)。

于 2008-12-24T00:35:57.203 に答える
3

SO にはすでに実質的に同じ質問がありますIs WHILE TRUE…BREAK…END WHILE a good design? . @Glomekは(過小評価された投稿で)答えました:

時にはそれはとても良いデザインです。いくつかの例については、Donald Knuth によるGoto ステートメントを使用した構造化プログラミングを参照してください。私はこの基本的な考え方を、「n 回半」実行されるループ、特に読み取り/処理ループでよく使用します。ただし、私は通常、break ステートメントを 1 つだけ持つようにしています。これにより、ループ終了後のプログラムの状態を簡単に判断できます。

しばらくして、私は関連する、またひどく過小評価されたコメントで返信しました(グロメックの最初のラウンドに気付かなかったことが一因だと思います):

魅力的な記事の 1 つは、1974 年の Knuth の「go to ステートメントを使用した構造化プログラミング」です (彼の著書「Literate Programming」や、おそらく他の場所でも入手できます)。とりわけ、ループから抜け出す制御された方法、および (用語を使用しないで) ループと半分のステートメントについて説明します。

Ada は、次のようなループ構造も提供します。

loopname:
    loop
        ...
        exit loopname when ...condition...;
        ...
    end loop loopname;

元の質問のコードは、意図的にこれに似ています。

参照されている SO アイテムとこれとの違いの 1 つは、「最終ブレーク」です。これは、ブレークを使用してループを早期に終了するシングルショット ループです。それが良いスタイルかどうかについても質問がありました - 私は手元に相互参照を持っていません.

于 2008-12-24T02:45:43.103 に答える
2

ポートでリッスンしたり、接続を待機したりするなど、無限ループが必要になる場合があります。

そのため、while(true)... を良いか悪いかで分類すべきではなく、状況に応じて何を使用するかを決定します

于 2008-12-24T07:30:43.997 に答える
1

例外ではないブレーク条件が1つ(そして1つだけ)ある場合は、その条件を制御フロー構造に直接(while)配置することをお勧めします。while(true){...}を見ると、コードリーダーとして、ブレーク条件を列挙する簡単な方法はないと思い、「これを注意深く見て、ブレーク条件(前に設定されているもの)について慎重に考えるようになります。それらは現在のループにあり、前のループに設定されている可能性があります)」

要するに、私は最も単純なケースであなたの同僚と一緒にいますが、while(true){...}は珍しいことではありません。

于 2008-12-24T01:16:33.207 に答える
1

完璧なコンサルタントの答え:それは状況によって異なります。ほとんどの場合、正しいことはwhileループを使用することです

while (condition is true ) {
    // do something
}

またはCのような言語で行われる「まで繰り返す」

do {
    // do something
} while ( condition is true);

これらのケースのいずれかが機能する場合は、それらを使用してください。

サーバーの内部ループのように、外部からの割り込みが発生するまでプログラムを続行する必要があることを意味する場合があります。(たとえば、httpdデーモンを考えてみてください。クラッシュするか、シャットダウンによって停止されない限り、停止することはありません。)

その後、そしてその時だけ、while(1)を使用します:

while(1) {
   accept connection
   fork child process
}

最後のケースは、終了する前に関数の一部を実行したいというまれなケースです。その場合は、次を使用します。

while(1) { // or for(;;)
   // do some stuff
   if (condition met) break;
   // otherwise do more stuff.
}
于 2008-12-24T01:28:25.867 に答える
1

「while(true)」を使用する利点は、特にこれらの終了条件がコードブロック内の異なる場所に表示される必要がある場合に、複数の終了条件を記述しやすくすることだと思います。ただし、私にとっては、コードがどのように相互作用するかを確認するためにコードをドライランする必要がある場合、混乱する可能性があります。

個人的にはwhile(true)を避けようとします。その理由は、以前に作成したコードを振り返ると、通常、実際のコードよりも実行/終了するタイミングを把握する必要があることに気付くためです。したがって、最初に「休憩」を見つける必要があるのは、私にとって少し面倒です。

複数の終了条件が必要な場合は、条件決定ロジックを別の関数にリファクタリングして、ループブロックがクリーンで理解しやすいように見えるようにする傾向があります。

于 2008-12-24T01:39:08.980 に答える
1

いいえ、ループをセットアップするときに終了条件を常に知っているとは限らないか、複数の終了条件がある可能性があるため、それは悪いことではありません。ただし、無限ループを防ぐには、より注意が必要です。

于 2008-12-24T06:07:35.693 に答える
1

何をしようとしているのかにもよりますが、一般的には while に条件文を入れることを好みます。

  • コード内に別のテストが必要ないため、より簡単です。
  • ループ内でブレークを探しに行く必要がないため、読みやすくなっています。
  • あなたは車輪を再発明しています。while の要点は、テストが true である限り何かを実行することです。ブレーク条件を別の場所に置くことで、それを覆すのはなぜですか?

デーモンまたはその他のプロセスが強制終了されるまで実行する必要がある場合は、while(true) ループを使用します。

于 2008-12-24T00:36:29.027 に答える
0

次のようなループを使用する

while(1){何かをする}

状況によっては必要です。組み込みシステムプログラミング(PIC、MSP430、DSPプログラミングなどのマイクロコントローラーを考えてください)を行う場合、ほとんどすべてのコードはwhile(1)ループになります。DSPをコーディングする場合、while(1){}が必要な場合があり、残りのコードは割り込みサービスルーチン(ISR)です。

于 2008-12-26T06:51:14.637 に答える
0

悪いのはwhile(true)の部分ではありませんが、それを中断するか、そこから抜け出さなければならないという事実が問題です。breakとgotoは、フロー制御の実際には受け入れられない方法です。

私もその要点がよくわかりません。プログラムの全期間をループするものでも、少なくとも、Quitと呼ばれるブール値、またはwhile(!Quit)...のようなループでループから適切に抜け出すためにtrueに設定したものを使用できます。任意のポイントでbreakを呼び出して、飛び出します。

于 2008-12-24T08:29:44.393 に答える
0

while(!)アプローチは、ループの意図をより明確かつ即座に伝えるため、私はそれを好みます。

于 2008-12-24T00:39:04.373 に答える
0

ここでは読みやすさと非常によく構成されていることについて多くの話がありましたが、サイズが固定されていない(つまり、whileとwhileを実行する)すべてのループと同様に、リスクがあります。

His reasoning was that you could "forget the break" too easily and have an endless loop.

whileループ内では、実際には、何かが発生しない限り無期限に実行されるプロセスを要求しています。特定のパラメーター内で何かが発生しない場合は、必要なものを正確に取得できます...無限のループ。

于 2008-12-24T01:30:45.707 に答える
0

あなたの友達がすすめるものは、あなたがしたものとは異なります。あなた自身のコードはより似ています

do{
    // do something
}while(!<some condition>);

条件に関係なく、常に少なくとも 1 回はループを実行します。

しかし、他の人が述べたように、休憩がまったく問題ない場合もあります。「休憩を忘れる」という友人の心配に応えて、私はよく次のような形で書きます。

while(true){
    // do something
if(<some condition>) break;
    // continue do something
}

適切なインデントにより、コードを初めて読む人にもブレークポイントが明確になり、ループの最初または最後でブレークするコードと同じように構造的に見えます。

于 2008-12-24T07:25:15.707 に答える
0

彼はおそらく正しい。

機能的には、この 2 つを同じにすることができます。

ただし、読みやすさとプログラム フローの理解のためには、while(条件) の方が優れています。休憩は、一種の後藤のようなものです。while (条件) は、ループを継続する条件などで非常に明確です。これは、break が間違っているという意味ではなく、読みにくくなる可能性があるだけです。

于 2008-12-24T00:36:58.217 に答える
0

私の頭に浮かぶ後者の構成を使用するいくつかの利点:

  • ループのコードの中断を探すことなく、ループが何をしているかを理解しやすくなります。

  • ループ コードで他のブレークを使用しない場合、ループには終了点が 1 つしかなく、それが while() 条件です。

  • 通常、最終的にコードが少なくなり、可読性が向上します。

于 2008-12-24T00:37:33.113 に答える