forループで<または<=を使用する必要があります[終了]


124

ループを7回繰り返す必要がある場合は、次のように使用しますか?

for (int i = 0; i < 7; i++)

または:

for (int i = 0; i <= 6; i++)

2つの考慮事項があります。

  • パフォーマンス
  • 読みやすさ

パフォーマンスのために、JavaまたはC#を想定しています。「以下」または「以下」が使用されているかどうかは重要ですか?別の言語についての洞察がある場合は、どちらかを示してください。

読みやすくするために、0ベースの配列を想定しています。

UPD: 0ベースの配列についての私の言及は、混乱しているかもしれません。配列要素の反復については話していません。単なる一般的なループです。

このマジック番号が何であるかを説明する定数を使用することについて、以下の良い点があります。したがって、「int NUMBER_OF_THINGS = 7」があった場合i <= NUMBER_OF_THINGS - 1、奇妙に見えます。


私は言うでしょう:配列全体を実行している場合は、左側の数値を減算したり、追加したりしないでください。
レターマン、

回答:


287

最初はより慣用的です。特に、(0ベースの意味で)反復回数を示します。1ベースのもの(JDBC、IIRCなど)を使用する場合、<=を使用したくなるかもしれません。そう:

for (int i=0; i < count; i++) // For 0-based APIs

for (int i=1; i <= count; i++) // For 1-based APIs

実際のコードでは、パフォーマンスの差はごくわずかであると予想します。


30
パフォーマンスに違いがないことがほぼ保証されています。x86などの多くのアーキテクチャには、「最後の比較で以下の値にジャンプする」という指示があります。パフォーマンスの違いを確認する最も可能性の高い方法は、適切に実装されていないある種のインタープリター型言語です。
ウェッジ

3
代わりに!=の使用を検討しますか?私は、ループカウンターとしてiを最も明確に確立し、他には何もないことを私は言うでしょう。
ユンチン

21
私は通常はしません。それはあまりにも不慣れです。また、ループ中に誰かが誤ってiをインクリメントすると、非常に長いループに入る危険性もあります。
Jon Skeet、

5
STLイテレーターを使用した汎用プログラミングでは、!=の使用が義務付けられています。それ(偶発的な二重増加)は私にとって問題ではありませんでした。インデックスについては、<(または>降順)がより明確であり、慣習的であることに同意します。
Jonathan Graehl、2009

2
<を使用して配列の長さでループする場合、JITは配列アクセスを最適化します(バインドされたチェックを削除します)。したがって、<=を使用するよりも高速です。私はそれをチェックしていません
configurator

72

これらのループはどちらも7回繰り返されます。あなたが他に本当に正当な理由がない限り、私はそれに7が付いている方がより読みやすく/より明確であると思います。


私が最初にJavaを学び始めたときのことを覚えています。私は常に1から始まるインデックスを使用してきたため、0から始まるインデックスの概念が嫌いでした。したがって、私は常に<= 6バリアントを使用します(質問に示されているように)。なぜなら、forループが実際に終了したときに、最終的には混乱するからです。それはちょうど<使用する方が簡単です
ジェームズ・ハウグ

55

私が大学で8086集会を行ったときから、実行する方がパフォーマンスが高かったことを覚えています。

for (int i = 6; i > -1; i--)

兆候がなければジャンプするというJNS操作があったためです。これを使用すると、比較値を取得するための各サイクルの後にメモリ検索がなく、比較もできませんでした。最近では、ほとんどのコンパイラーがレジスターの使用を最適化しているため、メモリーの問題はもはや重要ではありませんが、不要な比較が行われています。

ちなみに、ループに7または6を入れると、「マジックナンバー」が導入されます。読みやすくするために、意図を明らかにする名前を持つ定数を使用する必要があります。このような:

const int NUMBER_OF_CARS = 7;
for (int i = 0; i < NUMBER_OF_CARS; i++)

編集:人々はアセンブリのものを手に入れていないので、より完全な例が明らかに必要です:

(i = 0; i <= 10; i ++)の場合は、次のようにする必要があります。

    mov esi, 0
loopStartLabel:
                ; Do some stuff
    inc esi
                ; Note cmp command on next line
    cmp esi, 10
    jle exitLoopLabel
    jmp loopStartLabel
exitLoopLabel:

(int i = 10; i> -1; i--)の場合、次のようになります。

    mov esi, 10
loopStartLabel:
                ; Do some stuff
    dec esi
                ; Note no cmp command on next line
    jns exitLoopLabel
    jmp loopStartLabel
exitLoopLabel:

私がチェックしたところ、MicrosoftのC ++コンパイラはこの最適化を行いませんが、次の場合は行います。

for (int i = 10; i >= 0; i--) 

したがって、道徳は、Microsoft C ++†を使用していて、昇順または降順で違いがない場合、使用する必要があるクイックループを取得するためです。

for (int i = 10; i >= 0; i--)

これらのどちらかではなく:

for (int i = 10; i > -1; i--)
for (int i = 0; i <= 10; i++)

しかし、率直に言って、「for(int i = 0; i <= 10; i ++)」の可読性を取得することは、通常、1つのプロセッサコマンドがないことよりもはるかに重要です。

†他のコンパイラは別のことをするかもしれません。


4
「マジックナンバー」のケースは、<=よりも<を使用した方が良い理由をよく表しています。
Rene Saarsoo、2008年

11
別のバージョンは「for(int i = 10; i--;)」です。一部の人々は "for(int i = 10; i-> 0;)"を使用して、組み合わせ->が行くことを装っています。
Zayenz、2009

2
アセンブリコードの+1
ニューロ

1
しかし、実際にループカウンターを使用するようになったとき(たとえば、配列のインデックス付けなど)は、7-i逆方向のカウントから得られる最適化を損なうことになるでしょう。
リーライアン

@Lie、これは、アイテムを順方向に処理する必要がある場合にのみ適用されます。これらの種類のループのほとんどの操作では、ループ内のアイテムに好きな順序で適用できます。たとえば、値を検索する場合、リストの最後から開始して上に移動しても、リストの最初から下に移動しても重要ではありません(リストのどの端がアイテムであるかを予測できない場合)ありそうで、メモリキャッシングは問題ではありません)。
マーティンブラウン

27

<= array.length-1よりも読みやすいので、常に<array.lengthを使用します。

また、<7であり、インデックスが0で始まることがわかっている場合、その数は反復の数であることが直感的にわかるはずです。


ループで使用するときは、Length関数のコストを常に確認するように注意する必要があります。たとえば、C / C ++でstrlenを使用すると、比較にかかる時間が大幅に増加します。これは、strlenが文字列全体を反復してその答えを見つける必要があるためです。これは、ループのすべての反復ではなく、おそらく一度だけ実行したいものです。
マーティンブラウン

2
@Martin Brown:Java(およびC#だと思います)では、Stringは不変で、Arrayは不変の長さなので、String.lengthとArray.lengthは定数です。また、String.lengthとArray.lengthは(関数呼び出しではなく)フィールドであるため、O(1)である必要があります。C ++の場合、なぜ、そもそもなぜC-stringを使用しているのですか?
リーライアン

18

最適化の観点から見ると、それは問題ではありません。

コードスタイルの観点から見ると、私は<を好みます。理由:

for ( int i = 0; i < array.size(); i++ )

よりもはるかに読みやすいです

for ( int i = 0; i <= array.size() -1; i++ )

また、<はすぐに反復回数を提供します。

<へのもう1つの投票は、偶発的な1つ前のミスを防ぐことができるということです。


10

@ Chris、.NETで.Lengthが高額になるというあなたの声明は実際には真実ではなく、単純なタイプの場合は正反対です。

int len = somearray.Length;
for(i = 0; i < len; i++)
{
  somearray[i].something();
}

実際より遅い

for(i = 0; i < somearray.Length; i++)
{
  somearray[i].something();
}

後者は、ランタイムによって最適化されるケースです。ランタイムはiが配列への有効なインデックスであることを保証できるため、境界チェックは行われません。前者では、ランタイムはループの前にiが変更されなかったことを保証できず、すべてのインデックスルックアップに対して配列の境界チェックを強制します。


Javaでは、.Lengthが高価になる場合があります。 stackoverflow.com/questions/6093537/for-loop-optimization b'cozは.lenghtORを呼び出します.size。確かではありませんが、自信はありませんが、確認したいだけです。
Ravi Parekh、

6

パフォーマンスに関しては、効果的な違いはありません。したがって、私はあなたが解決している問題の文脈で理解しやすい方を使用します。


5

私は好む:

for (int i = 0; i < 7; i++)

これは、「ループを7回繰り返す」に簡単に変換できると思います。

パフォーマンスへの影響についてはよくわかりません。違いがコンパイルされると思います。


4

Java 1.5では、あなたはただ行うことができます

for (int i: myArray) {
    ...
}

そのため、配列の場合は心配する必要はありません。


4

パフォーマンスの違いはないと思います。2番目の形式は間違いなく読みやすくなっていますが、最後の反復数を見つけるために1を精神的に減算する必要はありません。

編集:私は他の人が同意しないと思います。個人的には、ループ構造の実際のインデックス番号を確認するのが好きです。多分それ0..6は私が知っているPerlの構文をより思い出させるからだと思います(0,1,2,3,4,5,6)。7が表示されている場合は、その横の演算子をチェックして、実際にはインデックス7に到達していないことを確認する必要があります。


「最後の反復回数」が「反復回数」よりも重要であると考えるかどうかによって異なります。0ベースのAPIの場合、それらは常に1
Jon Skeet

4

「<7」バージョンを使用すると思います。これは、大多数の人が読むためです。つまり、コードを読み飛ばしていると、誤って解釈される可能性があります。

「<」が「<=」より速いかどうかは気にしないで、読みやすくします。

速度を上げたい場合は、次の点を考慮してください。

for (int i = 0; i < this->GetCount(); i++)
{
  // Do something
}

パフォーマンスを向上させるには、次のように少し並べ替えます。

const int count = this->GetCount();
for (int i = 0; i < count; ++i)
{
  // Do something
}

GetCount()がループから削除され(すべてのループで照会されるため)、 "i ++"が "++ i"に変更されています。


読みやすいので2番目の方が好きですが、毎回this-> GetCount()を本当に再計算しますか?thisを変更するときに私はこれに気づき、カウントは同じままでdo..while this-> GetCount()を実行するように強制しました
osp70

GetCount()は、最初の例では反復ごとに呼び出されます。2番目の例では、1回だけ呼び出されます。あなたがいる場合必要な毎回呼び出されるGetCountのを()あなたが外でそれを維持しようとしない場合は、その後、ループでそれを持っています。
マークイングラム、

はい、私はそれを試してみました、そしてあなたは正しいです、私の謝罪。
osp70 2008年

i ++ではなく++ iを使用することでどのような違いがありますか?
Rene Saarsoo、2008年

intを使用すると速度が少し向上しますが、独自のクラスをインクリメントしている場合は、速度が大きくなる可能性があります。基本的に++ iは実際の値を増分し、実際の値を返します。i ++はtemp varを作成し、実際のvarをインクリメントしてからtempを返します。++ iではvarを作成する必要はありません。
Mark Ingram

4

C ++では、!=すべてのSTLコンテナーで使用できるを使用することを好みます。すべてのSTLコンテナーイテレーターが比較可能であるとは限りません。


これは、何かが私の意図する値を超えてカウンターを反復し、これが無限ループになるという非常にわずかな外部の可能性があるため、私を少し怖がらせます。可能性は遠く、簡単に検出されますが、<の方安全です。
ロブ・アレン

2
コードにそのようなバグがある場合は、おそらく黙って続行するよりもクラッシュして書き込む方がよいでしょう:-)

これは正しい答えです。イテレータへの要求が少なくなり、コードにエラーがある場合に表示される可能性が高くなります。<の引数は近視眼的です。70年代には、CPU時間にお金を払っていたときに、無限ループが悪いでしょう。'!='はバグを隠す可能性が低くなります。
David Nehme、2008年

1
イテレータのループは、カウンタのループとはまったく異なるケースです。!=はイテレータに不可欠です。
DJクレイワース2008年

4

Edsger Dijkstra 1982年にこの記事書いて、 <= i <upperであると主張しています:

最小の自然数があります。b)とd)のように下限を除外すると、前述のように、下限が最小の自然数から始まるサブシーケンスが不自然数の領域に強制されます。それは醜いので、下限についてはa)とc)のように≤を好みます。ここで、最小の自然数から始まるサブシーケンスを考えてみましょう。上限を含めると、シーケンスが空の数に縮小するまでに、上限が不自然になります。それは醜いので、上限についてはa)とd)のように<を好みます。慣習a)が優先されると結論付けます。


3

まず、6または7を使用しないでください。

使用するのが良い:

int numberOfDays = 7;
for (int day = 0; day < numberOfDays ; day++){

}

この場合、使用するよりも優れています

for (int day = 0; day <= numberOfDays  - 1; day++){

}

さらに良い(Java / C#):

for(int day = 0; day < dayArray.Length; i++){

}

さらに良い(C#)

foreach (int day in days){// day : days in Java

}

逆ループは確かに高速ですが、(他のプログラマーによってではないとしても)読むのが難しいため、回避することをお勧めします。特にC#では、Java ...


2

この場合7は理にかなっていると言って群衆に同意しますが、6が重要な場合は、6番目のインデックスまでのオブジェクトのみに作用していることを明確にしたいと言って追加します。 <=の方が6が見やすくなるので良いです。


そのため、i <= LAST_FILLED_ARRAY_SLOTと比較したサイズ<
Chris Cudmore

2

大学に戻ったとき、これら2つの操作がCPUの計算時間で似ていることを覚えています。もちろん、私たちはアセンブリレベルで話し合っています。

ただし、C#またはJavaを使用している場合、どちらか一方が他方よりも速度が向上することはないと思います。取得する数ナノ秒は、導入する混乱の価値がないと考えられます。

個人的には、ビジネス実装の観点から意味のあるコードを作成し、読みやすくします。


2

これは、「間違ったコードを間違ったものにするのカテゴリに直接該当します。

JavaやC#などのゼロベースのインデックス言語では、人々は index < count条件のます。したがって、この事実上の慣習を活用すると、1つずつずれたエラーがより明確になります。

パフォーマンスに関して:メモリフットプリントに値する優れたコンパイラは、問題のないものなどをレンダリングする必要があります。


2

少し余談ですが、.Netの配列やその他のコレクションをループすると、

foreach (string item in myarray)
{
    System.Console.WriteLine(item);
}

数値のforループより読みやすくします。もちろん、これは実際のカウンターInt自体がループコードで使用されていないことを前提としています。パフォーマンスに変化があるかどうかわかりません。


これには、ループ中にコレクションサイズを変更しないことも必要です。
ジェフB

したがって、For(i = 0、i <myarray.count、i ++)
Rob Allen

1

i <7と書くのには多くの理由があります。7回繰り返すループ内に数値7があると良いでしょう。パフォーマンスは実質的に同じです。ほとんど誰もがi <7と書いています。読みやすくするために書いている場合は、誰でもすぐにわかるフォームを使用してください。


1

私はいつも好みました:

for ( int count = 7 ; count > 0 ; -- count )

あなたの理論的根拠は何ですか?本当に興味があります。
Chris Cudmore、

これは、逆ループではまったく問題ありません。このようなものが必要になった場合
ShoeLace

1
1つの理由は、uPレベルでは0と比較して高速であることです。もう1つは、読みやすく、残りの回数を簡単に確認できることです。
ケニー

1

<を使用する習慣をつけると、配列を反復処理するときに、読者と読者の両方で一貫したものになります。誰もが標準的な慣習を持つほうが簡単です。また、0ベースの配列で言語を使用している場合、<が慣例です。

これは、ほぼ間違いなく、<と<=のパフォーマンスの違いよりも重要です。最初に機能性と可読性を目指し、次に最適化します。

もう1つの注意点は、フェッチとインクリメントには一時的なものを必要とし、フェッチとインクリメントには必要がないため、i ++ではなく++ iを行う習慣を身に付けた方がよいということです。整数の場合、コンパイラーは一時的なものを最適化する可能性がありますが、反復型がより複雑な場合、それができない可能性があります。


1

マジックナンバーを使わないでください。

なぜ7なのですか?(またはそのことについては6)。

使用したい番号に正しい記号を使用してください...

その場合、私は使用する方が良いと思います

for ( int i = 0; i < array.size(); i++ )

1

「<」および「<=」演算子は、まったく同じパフォーマンスコストです。

'<'演算子は標準であり、ゼロベースのループで読みやすいです。

i ++の代わりに++ iを使用すると、C ++ではパフォーマンスが向上しますが、C#ではパフォーマンスが向上しません-Javaについては知りません。


1

人々が観察したように、あなたが言及した2つの選択肢のどちらにも違いはありません。これを確認するために、JavaScriptで簡単なベンチマークを行いました。

ここで結果を見ることができます。これから明らかでないことは、1番目と2番目のテストの位置を入れ替えると、これら2つのテストの結果が入れ替わることになり、これは明らかにメモリの問題です。ただし、3番目のテスト(反復の順序を逆にするテスト)は明らかに高速です。


2番目のケースでなぜi = 1から始めるのですか?
ユージーンカッツ

うーん、ばかげた間違い?私はこれをかなり早く、おそらく15分で打ち上げました。
David Wees、2008年

1

誰もが言うように、配列の外でも、0インデックスのイテレータを使用するのが慣例です。すべてがで始まり、0で終わり、n-1下限が常に<=で上限が常に<である場合、コードをレビューするときに行う必要があることはそれほど考えられません。


1

すばらしい質問です。私の答え:タイプA( '<')を使用してください

  • 繰り返し回数がはっきりとわかります(7)。
  • 2つの端点の違いは、範囲の幅です
  • 文字が少ないほど読みやすくなります
  • 多くの場合、最後の要素のインデックスではi < strlen(s)なく、要素の総数があるため、均一性が重要です。

別の問題は、この全体の構造にあります。iその中に3回出現するため、タイプミスする可能性があります。forループ構造は、何をすべきかでなく、どのようにすべきかを示します。これを採用することをお勧めします:

BOOST_FOREACH(i, IntegerInterval(0,7))

これはより明確で、同じasm命令などを実行するようにコンパイルされます。必要に応じて、IntegerIntervalのコードを教えてください。


1

非常に多くの答えが...しかし、私は何か追加する必要があると信じています。

私の好みは、「i」がループで取る値を文字通りの数値で明確に示すことです。したがって、ゼロベースの配列を反復処理する場合:

for (int i = 0; i <= array.Length - 1; ++i)

また、配列を反復するのではなく、単にループしている場合は、1から7までの数え方はかなり直感的です。

for (int i = 1; i <= 7; ++i)

可読性は、それをプロファイリングするまでパフォーマンスに勝ります。おそらくそれまでコンパイラーまたはランタイムがコードをどう処理するかわからないからです。


1

!=代わりに使用することもできます。そうすることで、初期化でエラーが発生した場合に無限ループが発生し、エラーが早期に検出され、それによって発生する問題がループでスタックすることに限定されます(問題がずっと後になって見つけられないのではなく)。それ)。


0

どちらでも大丈夫だと思いますが、あなたが選んだらどちらか一方に固執してください。<=を使用することに慣れている場合は、<を使用しないようにしてください。逆も同様です。

私は<=を好みますが、ゼロから始まるインデックスで作業している状況では、おそらく<を使用してみます。しかし、それはすべて個人的な好みです。


0

論理的な観点から厳密に言えば、同等性をテストする正確な理由< countよりも効率的であると考える必要があります。<= count<=


私はそうは思いません、アセンブラでは、cmp eax、7 jl LOOP_STARTまたはcmp eax、6 jle LOOP_STARTの両方に同じサイクル数が必要です。
Treb

<=は!(>)として実装できます
JohnMcG

!(>)も2つの命令ですが、JLEとJLの両方が同じ数のクロックサイクルを使用するため、<と<=は同じ時間がかかります。
Jacob Krall、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.