「for」ループの条件としての「<」と「!=」


31

次のforループがあるとします*:

for (int i = 0; i < 10; ++i) {
    // ...
}

一般に次のように書くこともできます。

for (int i = 0; i != 10; ++i) {
    // ...
}

最終結果は同じですので、一方を他方に対して使用するための実際の議論はありますか?個人的には、i何らかの理由で問題が発生して値10をスキップする場合に前者を使用します。

*マジックナンバーの使用は許されませんが、これは単なる例です。


18
これらのいずれかに対する優れたソリューションは、矢印演算子を使用することint i = 10; while(i --> 0) { /* stuff */ }
です。– corsiKa

@glowcoder矢印演算子は私のお気に入りです
カーソンマイヤーズ

3
@glowcoder、素晴らしいですが、後ろから移動します。あなたはいつもそれを望んでいないかもしれません。

2
@SuperElectric、次のように解析されますwhile ((i--) > 0)
クリストファージョンソン

17
センサー入力のwhileループテストが!= MAX_TEMPであることに起因する労働災害についての(恐らく不思議な)物語があります。残念なことに、ある日、センサーの入力がMAX_TEMPを通過することなく、MAX_TEMP未満からMAX_TEMPを超えるまでになりました。プロセスは検出されずに過熱し、火災が発生しました。時々!=と<の間に違いがあります。
チャールズE.グラント

回答:


59

どちらかを選択する理由は、意図のためであり、その結果、読みやすさ向上します。

意図:ループはi、10にi等しくない限りではなく、10未満で実行される必要があります。そのように書かれている。

読みやすさ:意味を書き留めた結果、理解しやすくなりました。たとえば、を使用する場合i != 10、コードを読んでいる人がループ内にi10を超える何らかの方法があり、ループを続行する必要があるのではないかと疑問に思うかもしれません(ところで: - for文ですが、それは人々がそれをしないという意味ではなく、結果としてメンテナーはそれを期待しています)。


3
いいえ、ループiは「10より小さい」限り実行されるべきではなく、(この場合)上限に達しない限り実行されるべきです。C ++の間隔/イテレータ範囲の理論的根拠を使用する場合、を介してこれを表現する方がはるかに論理的!=です<
コンラッドルドルフ

14
@Konrad私はこれにまったく同意しません。要点は独断的ではなく、条件の意図を最もよく表す構成を選択することです。したがって、0から始まり上に移動するものをループすることを選択した場合、<実際にそれを正確に表現します。
デッカード

6
@コンラッド、あなたはポイントを逃しています。このループのインクリメント演算子により、ループ条件が同一性比較ではなく上限であることは明らかです。デクリメントしている場合、下限になります。順序付けられていないコレクションを繰り返し処理している場合、IDが適切な条件である可能性があります。
アレックスファインマン

3
@Alex増分は私のポイントではありませんでした。間隔に順序あるとは限りません。それはただ繰り返します…まあ、最後に達するまで何か。たとえば、ハッシュセットの要素。C ++では、これは反復子とforループを使用して行われます。<ここを使用するのはばかげているでしょう。また、デッカードはそれを認めているようです。私のコメントに対する彼のコメントに非常に同意します。
コンラッドルドルフ

1
あなたが追いかけるポインタをロータリーバッファを使用する場合は、あなたが使用しなければならない!=
SFを。

48

<パターンは、増分は正確に1ではないことが起こった場合でも、一般的に使用可能です。

これにより、実際の実行方法に関係なく、単一の一般的な方法でループを実行できます。


6
またi、必要に応じてループ内で完全に安全に変更できます。
マシューシャーリー

1
または「i」が完全に安全に変更されていない場合...別のチームに奇妙なサーバーの問題がありました。100%のCPU使用率を報告し続けましたが、サーバーまたは監視システムに問題があるはずです。いいえ、私たちが話しているのと同じ問題を抱えた「専門の上級プログラマー」によって書かれたループ条件を見つけました。
jqa

12
ただし、すべてのC ++ forループが<シーケンスの反復子などのを使用できるわけではないため、C ++でループ!=を実行する一般的な方法は1つだけです。
デビッド

@SnOrfus:私はそのコメントをまったく解析していません。一般に、イテレータの増分が多すぎる場合に例外を確実に取得することはありません(ただし、より具体的な状況があります)。
デヴィッドソーンリー

まあ、私は<パターンに従っています。>パターンでは2回、キーでは4 回ではなく1回のキーストロークが必要!=です。それは本当にOCDのような効率の問題です。
スポイケ

21

C ++ではより効果的なC ++(項目6)でのスコット・マイヤーズの勧告は、それはあなたがシームレスに切り替えることができますので、あなたはイテレータと整数インデックスの同じ構文を持っていることを意味するので、あなたがいないの理由がない限り秒を使用することが常にあるintとイテレータを構文に変更はありません。

他の言語ではこれは当てはまらない<ので、ThorbjørnRavn Andersenのポイントのせいでおそらくおそらく好ましいと思います。

ちなみに、先日別の開発者とこれについて話し合っていましたが、彼が好む理由は、誤って1つ以上インクリメントする可能性があり、ブレーク条件が満たされない可能性があるため<だと彼は言いました。それはIMOのナンセンスです。!=i


17
「無意味な負荷」...ループの本体に誤って余分なi ++が含まれる日まで。

2
特に、「大量のナンセンス」の場合、+ 1です。ループ本体が誤ってカウンターをインクリメントすると、はるかに大きな問題が発生します。
コンラッドルドルフ

12
@Bタイラー、私たちは人間にすぎず、以前より大きな間違いが発生しました。考えてみましょ<よりも防御的プログラミングであることを!=...そして

2
@ThorbjørnRavn Andersen-私はあなたに同意しないと言っているのではありません。<より防御的であり、私が書いた場合、私の同僚はそれを嫌い!=ます。ただし、!=C ++の場合があり、それは私が提示したものです。

9
偶発的な余分な結果になる可能性があるシナリオの1つはi++、whileループをforループに変更する場合です。ただし、反復回数の半分が(ほぼ)無限ループよりも優れているかどうかはわかりません。後者はおそらくバグをより明白にするでしょう。
ak2

12

私の意見では、(i <10)を使用する方が安全です。潜在的な終了ケースの最大数(10以上のすべて)をキャッチします。これを他のケース(i!= 10)と比較してください。iがちょうど10の場合、1つの可能な終了ケースのみをキャッチします。

これの副産物は、読みやすさを改善することです。さらに、増分が1以外であれば、終了ケースの作成時にミスを犯した場合の問題の可能性を最小限に抑えることができます。

考慮してください:

1) for (i = 1; i < 14; i+=2)

2) for (i = 1; i != 14; i+=2)

両方のケースに欠陥があるか間違っている可能性がありますが、2番目のケースは終了しないため、さらに間違っている可能性があります。最初のケースは終了し、おそらく14が間違った数ですが(15の方が良いでしょう)、正しい場所で終了する可能性が高くなります。


最初のケースは正しいかもしれません!14未満のすべての自然数を反復処理する場合、それを表現するより良い方法はありません。「適切な」上限(13)を計算するのは愚かなことです。
-maaartinus

9

まだ誰も思いついていないように思える別の答えがあります。 forシーケンスを反復処理する必要がある場合は、ループを使用する必要があります。使用!=は、ループの終了条件を記述する最も簡潔な方法です。ただし、制限の少ない演算子を使用することは、非常に一般的な防御プログラミングのイディオムです。整数の場合、それは重要ではありません-より具体的な例のない単なる個人的な選択です。!=他の人が述べた理由で、使用したいイテレータでコレクションをループします。floatまたはのシーケンスを検討する場合、すべてのコストdoubleを回避する必要!=があります。

私が指摘したいのはfor、シーケンスを繰り返す必要があるときに使用されることです。生成されたシーケンスには、開始点、間隔、および終了条件があります。これらは、forステートメント内で簡潔に指定されています。(1)のステップ部分が含まれていない、forまたは(2)trueガード条件として何かを指定している場合は、forループを使用しないでください!

whileループは、特定の条件が満たされている間、処理を継続するために使用されます。 シーケンスを処理していない場合は、while代わりにループが必要です。ガード条件の引数はここでは似ていますが、a whileforループの決定は非常に意識的なものでなければなりません。whileIMO C ++サークルに理解下ループです。

アイテムのコレクションを処理している場合(非常に一般的なfor-loopの使用法)、実際にはより特殊な方法を使用する必要があります。残念ながら、std::for_eachC ++ではいくつかの理由でかなり苦痛です。多くの場合、forループの本体を独立した関数で分離すると(多少痛みを伴いますが)、結果としてよりクリーンなソリューションになります。 コレクションで作業する場合、検討するstd::for_eachstd::transformまたはstd::accumulate この方法で表現すると、多くのアルゴリズムの実装が簡潔で明確になります。言うまでもなく、ループの本体を別の関数/メソッドに分離すると、アルゴリズム、その入力要件、および結果に専念することになります。

Java、Python、Ruby、またはC ++ 0xを使用している場合は、適切なコレクションforeachループを使用する必要があります。C ++コンパイラがこの機能を実装forすると、これらの種類の議論と同様に、多くのループがなくなります。


2
「ただし、制限の少ない演算子を使用することは、非常に一般的な防御プログラミングのイディオムです。」これは多かれ少なかれそれを合計します。
ジェームズP.

比較しての意図の違いをdiscussinための1 whilefor、およびforeach(または言語equivilant)
ケビンPeno

厳密に言えば、特定の条件がされるまでループ処理を継続するために使用されていない会ったwhile
オリオン座ゼータ星

@Alnitak-D'oh ...修正します:)
D.Shawley

「制限の少ない」という2つの可能な意味に混乱しました。それは、渡す値<に寛容な演算子()または失敗する値に寛容な演算子()を参照することができるということ!=です。
Mateen Ulhaq

4

「より小」を使用することは(通常)意味的に正しいため、実際にはカウントiが10以上になるまでカウントアップすることを意味するため、「より小」は意図を明確に伝えます。「等しくない」を使用することは、実質的にコールの場合に明らかに機能しますが、少し異なる意味を伝えます。場合によってはこれが必要な場合もありますが、私の経験ではこれは決してありませんでした。本当にi10を超える場合もあれば10未満になる場合もあるが、10に等しくなるまでループを続けたい場合、そのコードは実際に非常に明確にコメントする必要があり、おそらく他のコンストラクトで記述したほうがよいでしょう。whileおそらくループとして。


2

私がa less thanを好む理由の1つnot equalsは、ガードとして行動することです。一部の限られた状況(不適切なプログラミングまたはサニタイズ)では、まだ有効でnot equalsあるのにスキップできますless than

以下は、サニタイズチェックの欠如が奇妙な結果をもたらした1つの例です。http//www.michaeleisen.org/blog/? p = 358


0

ここでは、を使用する<よりもを使用することを好む理由の1つを示します!=。グローバル変数スコープを持つ言語を使用している場合、他のコードが変更されるとiどうなりますか?を使用<する場合!=、最悪の事態は反復がより速く終了することです。おそらく他のコードiが偶然に増分し、forループでいくつかの反復をスキップする可能性があります。しかし、0から10をループしている場合、ループは9になり、i奇妙な理由で誤って記述されたスレッドが増加します。その後、ループはその反復を終了しi、値が11になるように増分します。ループが終了条件をスキップしたため(i10に等しくなることはありません)、無限にループします。

これを引き起こすのは、特に奇妙なスレッド化およびグローバル変数型のロジックである必要はありません。バックトラックする必要があるループを書いているだけかもしれません。iループ内で突然変異を起こしてロジックを台無しにした場合、aではなく上限を持つようにすると!=、無限ループに陥る可能性が低くなります。


3
もちろん、これはfor-eachループの完全な議論であり、一般的にはより機能的なプログラミングスタイルのようです。しかし、可変変数の方がずっと楽しいのに、なぜそれをしたいのでしょうか?!
トムモリス

3
それは恐ろしい理由だとは思わない。いずれにせよ、バグを見つけて修正する必要がありますが、無限ループはバグを明らかにする傾向があります。
ak2

0

組み込みの世界、特にノイズの多い環境では、RAMが正常に動作することを期待することはできません。「==」または「!=」を使用して比較する条件付き(for、while、if)では、ループを終了する重要な値を変数がスキップするリスクが常に発生します。これは、重大な結果を招く可能性があります-Mars Landerレベルの結果。条件で「<」または「>」を使用すると、「未知の未知数」をキャッチするための安全性がさらに高まります。

元の例では、i10をはるかに超える値に不可解にカタパルトされた場合、「<」比較はエラーをすぐにキャッチしてループを終了しますが、「!=」はカウントアップを続けますi 0を過ぎて戻って戻るますから10。


2
別の関連するバリエーションとしてfor (i=4; i<length; i++) csum+=dat[i];、正当なパケットには常にlength少なくとも4つのパケットがありますが、破損したパケットを受信する可能性があるコードなどがあります。異なるパケットタイプに異なる長さの要件がある場合、パケットタイプごとに異なる処理の一部として長さを検証することをお勧めしますが、最初に共通ロジックの一部としてチェックサムを検証します。長さが不足しているパケットを受信した場合、チェックサム計算で「良い」または「悪い」が報告されるかどうかは実際には関係ありませんが、クラッシュすることはありません。
supercat
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.