web-dev-qa-db-ja.com

while(true)は、悪いプログラミングの練習を中断しますか?

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

while(true) {

    //do something

    if(<some condition>) {
        break;
    }

}   

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

while(!<some condition>) {

    //do something

}   

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

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

誰かがどちらかの側の証拠を追加することによってこの議論を豊かにすることができますか?

50
Edward Tanguay

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

探しているのはdo-whileループだと思います。 while (true)は、このコードを維持するのが難しくなり、ループをエスケープする方法が非常に悪いgoto esqueであるため、良いアイデアではないことに100%同意します。

試してください:

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

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

43

Wordsworthが過ぎ去った日々の有名な開発者を引用すると:

...
実際には、私たちが運命づける刑務所
私たち自身、刑務所はありません。したがって、私にとっては、
雑多な気分では、「娯楽に縛られる
ソネットのわずかな地面のプロット内。
一部の魂に満足している(そのようなニーズがあるに違いない)
自由の重さを感じた人、
私が見つけたように、そこに短い慰めを見つけるべきです。

ワーズワースは、ソネットの厳格な要件をストレートジャケットとしてではなく、解放フレームとして受け入れました。 「構造化プログラミング」の核心は、自由に理解しやすくするために、任意に複雑なフローグラフを作成する自由を放棄することだと思います。

時々早期終了がアクションを表現する最も簡単な方法であることに私は自由に同意します。しかし、私の経験では、可能な限り単純な制御構造を使用することを強制した場合(および実際にこれらの制約内で設計することを考えた場合)、結果はよりシンプルで明確なコードであることがほとんどです。の欠点

_while (true) {
    action0;
    if (test0) break;
    action1;
}
_

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

28
joel.neely

フォームにコードを記述できる場合

_while (condition) { ... }
_

または

_while (!condition) { ... }
_

withno exits(breakcontinue、またはgoto)、body、その形式誰かがコードを読んで、ヘッダーを見るだけで終了条件を理解できるためが推奨されます。それは良い。

しかし、多くのループはこのモデルに適合せず、中間に明示的な終了がある無限ループは名誉あるモデルです。 (continueを含むループは、通常breakを含むループよりも理解するのが困難です。)何らかの証拠や​​権限を引用したい場合は、Don Knuthの有名な論文 Structured Programming Gotoステートメントを使用) ;必要なすべての例、引数、および説明があります。

ちょっとしたイディオム:while (true) { ... }を書くことは、古いPascalプログラマーとして、あるいは最近ではおそらくJavaプログラマー。あなたが書いている場合CまたはC++では、推奨されるイディオムは

_for (;;) { ... }
_

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

20
Norman Ramsey

私は好む

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

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

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

多くのbreakポイントの場合、それらを

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

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

11
Bill the Lizard

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()
     }  
     ........

   }
7
Martin Beckett

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

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

そして、300文字で十分に応答できませんでした(ごめんなさい!)

私の指導と指導では、「複雑さ」を非公式に「この単一の行または表現を理解できるようにするために頭の中で必要な残りのコードの量」と定義しました。心に留めておく必要があるものが多いほど、コードは複雑になります。コードが明示的に教えてくれるほど、複雑さは減ります。

それで、複雑さを減らすことを目標に、純粋さよりも完全性と強さの観点からハビエルに返事させてください。

私はこのコードの断片を考えます:

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

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

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

違いは視点の1つです。これらの最初のものは一般にループ全体の外側の動的な振る舞いに関係していますが、2番目のものは特に_a1_について考えながら当てにすることができる内側の静的な保証を理解するのに役立ちます。もちろん、_a1_の正味の効果は_c1_を無効にする可能性があり、ポイント2などで期待できることについてもっと考える必要があります。

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

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

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

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

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

_while (true) {
    // p1
    a1;
    // p2
    ...
}
_

両方の問題を失望させます。外側のレベルでは、これが、私が実際にがこのループを永久に循環させることを意味するのかどうか(例えば、オペレーティングシステムのメインイベントディスパッチループ)なのか、他に何かが起こっているのかどうか疑問に思っています。これにより、本文を読むための明示的なコンテキストも、(不確実な)終了に向けた進歩を構成するものへの期待も与えられません。

内部レベルでは、ポイント1で保持される可能性のある状況についてabsolutely no明示的な保証があります。もちろんtrueという条件はどこでも当てはまりますが、プログラムのどの時点でも知ることができます。アクションの前提条件を理解することは、アクションが何を達成するかを考えようとするときに非常に貴重な情報です!

したがって、上記のロジックによれば、while (true) ...イディオムはwhile (c1) ...よりもはるかに不完全で弱いため、より複雑であることをお勧めします。

5
joel.neely

最初の方法は、ループを中断する方法が多数ある場合、またはループの上部で中断条件を簡単に表現できない場合(たとえば、ループの内容を途中で実行する必要があるが、残りの半分を実行してはならない場合) 、最後の反復で)。

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

3
yfeldblum

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

したがって、(true)...は良いか悪いかに分類されるべきではなく、状況に使用するものを決定させてください

2
JRomio

問題は、すべてのアルゴリズムが「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個の要素に(n-1)コンマが必要です。

While-loopモデルに固執するためのトリックをいつでも行うことができますが、これは常にコードを繰り返したり、同じ条件を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と混合することにより、コードを難読化することができます。そのため、彼はこれをしていないと感じることができます。

2
Vouze

のSOには、実質的に同一の質問が既にありますか?良いデザインである間、真である... BREAK ... END? 。 @Glomekは答えました(過小評価された投稿で):

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

少し後に、関連するコメントに返信しましたが、残念なことに過小評価されていました(Glomekの最初のラウンドに気づかなかったからだと思います)。

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

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

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

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

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

2

それはあなたが何をしようとしているのかにもよりますが、一般的には条件文をしばらくの間置くほうが好きです。

  • コード内に別のテストが必要ないため、より簡単です。
  • ループ内で休憩するために狩りに行く必要がないため、読みやすくなります。
  • あなたは車輪を再発明しています。 while全体のポイントは、テストが真である限り、何かをすることです。なぜブレーク条件を他のどこかに置くことでそれを覆すのでしょうか?

デーモンや、強制終了されるまで実行する他のプロセスを記述している場合は、while(true)ループを使用します。

1
ieure

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

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

1
Larry OBrien

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

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

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

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

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

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

THEN AND ONLY THEN while(1)を使用:

while(1) {
   accept connection
   fork child process
}

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

while(1) { // or for(;;)
   // do some stuff
   if (condition met) break;
   // otherwise do more stuff.
}
1
Charlie Martin

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

個人的にはwhile(true)を避けようとします。その理由は、以前に書かれたコードを振り返るときはいつも、実際に実行する以上のことを実行/終了するタイミングを把握する必要があることを発見するからです。そのため、最初に「休憩」を見つける必要があるのは少し面倒です。

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

1
Conrad

いいえ、ループのセットアップ時に終了条件が常にわからない場合や、複数の終了条件がある場合があるため、それは悪くありません。ただし、無限ループを防ぐにはさらに注意が必要です。

1
mcrute

友人が推奨することは、あなたがしたこととは異なります。あなた自身のコードはより似ています

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

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

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

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

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

0
billyswong

のようなループを使用して

while(1){何かをする}

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

0
devin

彼はおそらく正しいでしょう。

機能的には、この2つは同一です。

ただし、読みやすさおよびプログラムフローを理解するには、while(条件)の方が優れています。ブレークは、一種の後藤のようなものです。 while(条件)は、ループを継続する条件などで非常に明確です。これは、ブレークが間違っていることを意味するものではなく、読みにくい場合があります。

0
Doug T.

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

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

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

  • 通常、コードが少なくなり、読みやすくなります。

0
codelogic

forループの方が好きです:

_for (;;)
{
    ...
}
_

あるいは

_for (init();;)
{
    ...
}
_

ただし、init()関数がループの本体のように見える場合は、

_do
{
    ...
} while(condition);
_
0
Alexandre C.

While(!)アプローチを好むのは、ループの意図をより明確かつ即座に伝えるからです。

0
toad

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

私はまた、ポイントを本当に見ていません。プログラムの期間全体をループするものであっても、少なくともQuitと呼ばれるブール値、またはwhile(!Quit)...のようなループでループから適切に抜け出すためにtrueに設定するもののようなものを持つことができます。任意のポイントでブレークを呼び出して飛び出すだけで、

0
Gene Roberts

ここでは読みやすさについて多くの話があり、非常にうまく構築されていますが、サイズが固定されていないすべてのループ(つまり、whileおよびwhile)は危険にさらされます。

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

Whileループ内では、実際に何かが発生しない限り無期限に実行されるプロセスを要求しており、特定のパラメーター内で何かが発生しなければ、まさに望み通りの結果が得られます...無限ループ。

0
Ólafur Waage