20

レビューするとき、私は時々この種のループに遭遇します:

i = begin
while ( i != end ) {    
   // ... do stuff
   if ( i == end-1 (the one-but-last element) ) {
      ... do other stuff
   }
   increment i
}

それから私は質問をします:あなたはこれを書きますか?

i = begin
mid = ( end - begin ) / 2 // (the middle element)
while ( i != end ) {    
   // ... do stuff
   if ( i > mid ) {
      ... do other stuff
   }
   increment i
}

私の意見では、これはループを作成する意図に反します。要素ごとに共通の処理が行われるため、ループします。この構成を使用して、いくつかの要素に対して、何か別のことをします。したがって、これらの要素には別のループが必要であると結論付けます。

i = begin
mid = ( end - begin ) / 2 //(the middle element)
while ( i != mid ) {    
   // ... do stuff
   increment i
}

while ( i != end ) {
   // ... do stuff
   // ... do other stuff
   increment i
}

今、私はSOについて、-clauseをうまく書く方法についての質問さえ見ました...そして私は悲しくなりました:何かがここにありません。if

私が間違っている?もしそうなら、コーディング時にループ本体を特別なケースで乱雑にすることの何が良いのでしょうか?

4

13 に答える 13

23

この質問は、原則によって答えられるべきではないと思います (たとえば、「ループでは、すべての要素を同等に扱う」)。代わりに、実装が良いか悪いかを評価するために、次の 2 つの要因を調べることができます。

  1. 実行時の有効性 - コンパイルされたコードは高速に実行されますか?それとも別の方法で実行した方が高速でしょうか?
  2. コードの保守性 - (別の開発者にとって) ここで何が起こっているかを理解するのは簡単ですか?

すべてを 1 つのループで実行する方が速く、コードが読みやすい場合は、そのようにします。遅くて読みにくい場合は、別の方法で実行してください。

より速くて読みにくい場合、または遅くても読みやすい場合は、特定のケースでどの要因がより重要であるかを見つけてから、ループする方法 (またはループしない) を決定します。

于 2008-10-01T08:58:37.553 に答える
11

人々が配列の要素をカンマ区切りの文字列に結合しようとしたときに、これを見たことがあることを私は知っています:

for(i=0;i<elements.size;i++) {
   if (i>0) {
     string += ','
   }
   string += elements[i]
}

そこに if 句があるか、最後に文字列 += 行を再度複製する必要があります。

この場合の明らかな解決策は、

string = elements.join(',')

しかし、join メソッドは内部で同じループを実行します。そして、あなたが望むことをする方法が常にあるとは限りません。

于 2008-10-01T08:11:28.120 に答える
7

@xtofl、

私はあなたの懸念に同意します。

何百万回も同様の問題に遭遇しました。

開発者は、最初または最後の要素に特別な処理を追加します。

ほとんどの場合、startIdx + 1またはendIdx - 1要素からループするか、1 つの長いループを複数の短いループに分割するだけの価値があります。

ごくまれに、ループを分割できないことがあります。

私の意見では、珍しいことは可能な限りループの外で処理する必要があります。

于 2008-10-01T08:15:34.037 に答える
6

あなたが投稿した最後のスニペットでは、 // .... do stuff のコードを繰り返しています。

異なる一連のインデックスに対して完全に異なる一連の操作がある場合、2 つのループを維持することは理にかなっています。

i = begin
mid = ( end - begin ) / 2 //(the middle element)
while ( i != mid ) {    
   // ... do stuff
   increment i
}

while ( i != end ) {
   // ... do other stuff
   increment i
}

これは当てはまりませんが、1 つのループを保持する必要があります。ただし、( end - begin ) / 2 回の比較を保存するという事実は残っています。つまり、コードをきれいに見せたいのか、それとも CPU サイクルを節約したいのかということになります。電話はあなたのものです。

于 2008-10-01T09:03:53.797 に答える
6

特殊なケースを for ループに入れると、自分の利益のために賢すぎることがよくあることに気づきました。

于 2008-10-01T08:12:32.063 に答える
5

私はあなたがそれを完全に釘付けにしていると思います。ほとんどの人は、条件付きブランチをループに含めるという罠に陥りますが、それらを外部で実行できる場合は、単純に高速です。

例えば:

if(items == null)
    return null;

StringBuilder result = new StringBuilder();
if(items.Length != 0)
{
    result.Append(items[0]); // Special case outside loop.
    for(int i = 1; i < items.Length; i++) // Note: we start at element one.
    {
        result.Append(";");
        result.Append(items[i]);
    }
}
return result.ToString();

そして、あなたが説明した真ん中のケースは、単純に厄介です。そのコードが大きくなり、さまざまなメソッドにリファクタリングする必要がある場合を想像してみてください。

XMLを解析する場合を除いて、<grin>ループはできるだけ単純かつ簡潔に保つ必要があります。

于 2008-10-01T10:01:08.277 に答える
3

私は単純に、要素をループから除外し、ループの外側で分離処理を行うことを好みます

例:EOFの場合を考えてみましょう

i = begin
while ( i != end -1 ) {    
   // ... do stuff for element from begn to second last element
   increment i
}

if(given_array(end -1) != ''){
   // do stuff for the EOF element in the array
}
于 2013-04-06T05:55:55.957 に答える
3

ループがすべての要素を均等に処理することを意図していることについて、あなたは正しいと思います。残念ながら、特殊なケースがあり、これらは if ステートメントを介してループ構成内で処理する必要があります。

ただし、多くの特殊なケースがある場合は、別々の構造で 2 つの異なる要素セットを処理する方法を考える必要があります。

于 2008-10-01T08:12:40.783 に答える
2

もちろん、引き出せるようにループ状のものを特別に収納するのはばかげています。ただし、do_stuff も複製しません。コードをコピーして貼り付けないように、関数またはマクロに配置します。

于 2008-10-01T08:08:38.407 に答える
2

どちらがより良いパフォーマンスを発揮しますか?

アイテムの数が非常に多い場合、特にすべてのアイテムに対して何らかの操作を実行する場合は、常に 1 回ループします。条件を評価するコストは、2 回ループするよりも少ない可能性があります。

おっと、もちろん、2 回ループしているわけではありません... その場合は、2 回ループすることが望ましいです。ただし、主な考慮事項はパフォーマンスであるべきだと私は主張します。ループ境界の単純な操作 (1 回) で作業を分割できる場合は、ループ (N 回) で条件分岐を実行する必要はありません。

于 2008-10-01T08:10:40.523 に答える
2

もう 1 つ見たくないのは、for-case パターンです。

for (i=0; i<5; i++)
{
  switch(i)
  {
    case 0:
      // something
      break;
    case 1:
      // something else
      break;
    // etc...
  }
}

私は実際のコードでこれを見てきました。

于 2008-10-01T08:14:43.883 に答える
1

必要性と利便性に応じて使用するだけです。要素を平等に扱うこと自体は言及されておらず、言語が提供する機能を悪用しても害はありません。

于 2008-10-01T09:10:25.387 に答える
1

特殊なケースは、一度だけ実行する場合は、ループの外で実行する必要があります。

ただし、スコープのためにループ内に保持する方が簡単なインデックスまたはその他の変数が存在する場合があります。データ構造のすべての操作をループ制御構造内にまとめる文脈上の理由もあるかもしれませんが、それ自体は弱い議論だと思います。

于 2008-10-01T09:06:22.027 に答える