27

このトピックについて人々がどのような考えを持っているのか、私はただ興味があります. オブジェクトの配列があり、それらをループして、オブジェクトに特定の値が含まれているかどうかを確認し、含まれている場合はループを停止したいとします。ブレーク付きの for ループと条件付きループのどちらがより良い方法ですか?

私が提供した例の疑似コードは、議論のためだけのものです (これも ActionScript で書かれています。なぜなら、それが私の最近の主要言語だからです)。また、構文に関するベスト プラクティスのアイデアを探しているわけではありません。

ブレーク付き for ループ:

var i:int;

var isBaxterInMilwaukee:Boolean;    

for (i = 0; i < arrayLen; i++)
{
    if (myArray[i]["name"] == "baxter"
         && myArray[i]["location"] == "milwaukee")
    {
        isBaxterInMilwaukee = true;

        barkTwice();

        break;
    }
}

条件付きループ:

var i:int;

var isBaxterInMilwaukee:Boolean;    

while (!isBaxterInMilwaukee && i < arrayLen)
{
    if (myArray[i]["name"] == "baxter"
         && myArray[i]["location"] == "milwaukee")
    {
        isBaxterInMilwaukee = true;

        barkTwice();
    }

    i++;
}
4

18 に答える 18

32

要するに、最も読みやすく保守しやすいバージョンを使用する必要があります。

少し古い時代には、ループから抜け出すことは (goto ステートメントと同等に) ノーノーと見なされていたことを知っています。ループは、ループ条件でブレークし、それ以外の場所ではブレークしないはずでした。したがって、while ループが最適な方法でした。

(これはおそらくアセンブリからの持ち越しであり、ループは基本的に、最後に真の場合は最初にジャンプするステートメントを持つコードのブロックです。ブロック内の複数の条件付きジャンプ ステートメントにより、デバッグが非常に難しくなります。 ; したがって、それらは回避され、最後に 1 つにまとめられることになっていました。)

特に foreach ループと管理された世界では、この考え方が今日少し変化しているように感じます。今は本当にスタイルの問題です。ブレーク オン ファウンド for ループは、おそらく一部の純粋主義者を除いて、おそらく多くの人に受け入れられるようになっています。ただし、これはループ条件を混乱させ、混乱を招く可能性があるため、while ループで break を使用することは避けます。

foreach ループの使用を許可していただければ、以下のコードはwhile ループの兄弟よりもはるかに読みやすいと思います。

bool isBaxterInMilwaukee;    

foreach (var item in myArray)
{
    if (item.name == "baxter" && item.location == "milwaukee")
    {
        isBaxterInMilwaukee = true;    
        barkTwice();
        break;
    }
}

ただし、ロジックが複雑になるにつれて、break埋もれて見つけにくくならないように、ステートメントの近くに目立つコメントを配置することを検討することをお勧めします。


ほぼ間違いなく、この全体を独自の関数にリファクタリングする必要があります。この関数は見つかりませんbreakが、実際にreturnは結果になります (代わりに for ループ バージョンを自由に使用してください)。

bool isBaxterInMilwaukee(Array myArray)
{      
    foreach (var item in myArray)
    {
        if (item.name == "baxter" && item.location == "milwaukee")
        {
            barkTwice();
            return true;
        }
    }
    return false;
}

Esko Luontola が指摘したように、barkTwice()副作用は関数の名前から明らかではなく、すべてのケースで Baxter を見つけることにも関連していないため、呼び出しをこの関数の外に移動するのがおそらく最善でしょう。(または、ブール値のパラメーターを追加しBarkTwiceIfFound、行を読み取りに変更しif(BarkTwiceIfFound) barkTwice();て、副作用を明確にします。)


記録のために、休憩なしで for ループでフラグ チェックを実行することもできますが、for ループ定義で追加の条件を想定していないため、これは実際には読みやすさを損なうと感じています。

var i:int;

var isBaxterInMilwaukee:Boolean;    

for (i = 0; !isBaxterInMilwaukee && i < arrayLen; i++)
{
    if (myArray[i]["name"] == "baxter"
         && myArray[i]["location"] == "milwaukee")
    {
        isBaxterInMilwaukee = true;    
        barkTwice();
    }
}

while ループを使用して自動インクリメント メカニズムをシミュレートすることもできます。私はいくつかの理由でこれが好きではありません -i実際の開始値よりも 1 小さい値に初期化する必要があり、コンパイラがループ条件ロジックをどのように短絡するかによって、iループ終了時の値が異なる場合があります。それにもかかわらず、それは可能であり、一部の人々にとっては、これにより可読性が向上します。

var i:int = -1;

var isBaxterInMilwaukee:Boolean;    

while (!isBaxterInMilwaukee && ++i < arrayLen)
{
    if (myArray[i]["name"] == "baxter"
         && myArray[i]["location"] == "milwaukee")
    {
        isBaxterInMilwaukee = true;
        barkTwice();
    }
}
于 2009-02-27T17:18:45.883 に答える
8

コード内での使用は常に嫌いでしbreaksた... この場合は問題ではないようですが、より複雑なループでは、別のコーダーがそれを読んで非常に混乱する可能性があります。一般に、コーダーがbreakループの奥深くにネストされたものを見つけるまで、ループがどのように終了するかを理解できないという結果になることがよくあります。ループの各反復でチェックされるフラグ条件を指定することで、これがより明確になります。

この問題は、(変数をreturn設定してメソッドの最後に戻るのではなく) 簡単に見つけられないメソッドの本体の奥深くにあるステートメントを持つことに似ています。retVal小さなメソッドではこれで問題ないように見えますが、大きくなるほど混乱が大きくなります。

運用効率ではなく、保守性です。

特定の状況で何が読みやすく、理解しやすいかを同僚に尋ねてください。それが本当に重要なことです。

于 2009-02-27T17:09:00.260 に答える
5

依存していると言えます。この場合、ブレークのあるループはより明確に見えます。

于 2009-02-27T17:08:44.957 に答える
4

両者には概念的な違いがあります。forループは離散セットを反復するためのものであり、whileループは条件に基づいてステートメントを繰り返すためのものです。他の言語では、 orfinallyのような句やループ構造が追加されます。従来のループがかなり少ない傾向があります。foreachuntilfor

いずれにせよ、私が使用するルールは、forループは反復し、whileループは繰り返すというものです。次のようなものが表示された場合:

while (counter <= end) {
   // do really cool stuff
   ++counter;
}

次に、for反復しているため、おそらくループを使用したほうがよいでしょう。ただし、ループは次のようになります。

for (int tryCount=0; tryCount<2; ++tryCount) {
    if (someOperation() == SUCCESS) {
       break;
    }
}

while条件が真になるまで実際に何かを繰り返すため、ループとして記述する必要があります。

それは非常に無意味であるのと同じくらい悪いbreakので、使用しないという考え。それでは、例外をスローすることをどのように正当化できますか? それは単なる非ローカルで非決定論的な goto です! ところで、これは例外処理に対する暴言ではなく、ただの観察です。goto

于 2009-02-27T17:57:08.693 に答える
4

for ループでは、for ループ宣言に早期終了基準を入れることで、早期終了することもできます。したがって、あなたの例では、次のようにすることができます:

var i:int;

var isBaxterInMilwaukee:Boolean;    

isBaxterInMilwaukee = false;

for (i = 0; i < arrayLen && !isBaxterInMilwaukee; i++)
{
    if (myArray[i]["name"] == "baxter"
        && myArray[i]["location"] == "milwaukee")
    {
        isBaxterInMilwaukee = true;

        barkTwice();
    }
}

そうすれば休憩は必要なく、while ループよりも読みやすくなります。

于 2009-02-27T17:16:37.963 に答える
3

両方のループにブレークが見られますが、それは正しいですか?

ともかく:

  • ループが開始する前に既知の反復回数 (最大回数) がある場合は、FOR ループを選択します。
  • それ以外の場合は、WILE を選択します。
  • FORループではBREAKを自由に使っています。
  • WHILE ループでは、可能であれば、BREAK の代わりに複雑な条件を使用することを好みます。
于 2009-02-27T17:14:10.480 に答える
3

最も理にかなっているのは、コードを最もよく読んでいる人間にアイデアを伝えるものです。最初にコードの読みやすさを覚えておいてください。そうすれば、通常は正しい選択をすることができます。break通常、本当に必要な場合以外は、 のようなものを使用したくありません。これは、頻繁に使用したり、深くネストされた式のセットで使用したりすると、理解が困難になる可能性があるためです。continueブレークと同じ目的を果たすことができ、ループが壊れたという理由ではなく、通常どおり終了します。この場合、私がこれを書くかもしれないいくつかの異なる方法があります。

おそらく、ここで必要なのはwhileループの変更です。

while(!isBaxterInMilwaukee || i < arrayLen) {
  if(myArray[i]["name"] == "baxter" && myArray[i]["location"] == "milwaukee") {
    isBaxterInMilwaukee == true;
    barkTwice()
  } else {
    i++;
  }
}

breakこれは明確でorを使用していないためcontinue、式で指定された条件のいずれかの結果として常に終了することが一目でわかりますwhile

ETAi < arrayLen : おそらくループ内にあるはずです。whileそれ以外の場合、入力値がターゲット値と同じでない限り、最初は失敗します...

于 2009-02-27T17:12:22.763 に答える
2

私は間違いなくfor+breakで行きます。'for'は、「iterate over sequence」を即座に認識できるイディオムであり、「iterateoversequence」の方が理解しやすいです。ループと停止を組み合わせた条件よりも、値が見つかった場合は早期に終了します。

条件付きループコードで2つの間違いを犯したように見える方法で、これの証拠があるかもしれません!

  • while条件(!isBaxterInMilwaukee || i == arrayLen)—「(!(isBaxterInMilwaukee || i == arrayLen))」という意味ですか?

  • 終了ループ変数を使用している場合は、breakステートメントは不要です。

個人的には、終了ループ変数を追跡しようとするよりも、単純な「ブレーク」の方がはるかに読みやすいと思います。

于 2009-02-27T17:37:18.227 に答える
2

問題には次の 2 つの側面があります。

  • をすべきか (例: 項目の 1 つに指定された人物がその場所に含まれているかどうかを調べる)
  • それを行う方法(例:インデックスを使用する、反復するなど)

どちらの例も 2 つが混在していて、 howからwhatを理解するのは困難です。whatの部分だけをコードで表現できれば最高です。仕様パターンを使用してこれを行う例 (c# 3.5) を次に示します。

// what we are looking for?
IsPersonInLocation condition = new IsPersonInLocation("baxter", "milwaukee");

// does the array contain what we are looking for?
bool found = myArray.Find(item => condition.IsSatifiedBy(item));

// do something if the condition is satisfied
if (found) {
    barkTwice();
}

完全を期すために、条件のクラス定義を次に示します。

class IsPersonInLocation {
    public string Person { get; set; }
    public string Location { get; set; }
    public IsPersonInLocation(string person, string location) {
        this.Person = person;
        this.Location = location;
    }
    bool IsSatifiedBy(item) {
        return item["name"] == this.Person
            && item["location"] == this.Location;
    }
}
于 2009-02-27T20:07:59.127 に答える
2

私は休憩、より明確に言うでしょう(ループから抜け出す理由をコメントに入れたとしても)私見ながらwhileループは明確ではありません、私は休憩に行きます

于 2009-02-27T17:12:52.887 に答える
1

独自のメソッドでループをカプセル化し、一致条件が成功したときに return を使用して処理を終了します。

C# コードの例:

class Program
{
   static bool IsBaxterInMilwaukee(IList<WhoAndWhere> peopleAndPlaces)
   {
      foreach (WhoAndWhere personAndPlace in peopleAndPlaces)
      {
         if (personAndPlace.Name == "Baxter" 
            && personAndPlace.Location == "Milwaukee")
         {
            return true;
         }
      }
      return false;
   }

   static void Main(string[] args)
   {
      List<WhoAndWhere> somePeopleAndPlaces = new List<WhoAndWhere>();
      somePeopleAndPlaces.Add(new WhoAndWhere("Fred", "Vancouver"));
      somePeopleAndPlaces.Add(new WhoAndWhere("Baxter", "Milwaukee"));
      somePeopleAndPlaces.Add(new WhoAndWhere("George", "London"));

      if (IsBaxterInMilwaukee(somePeopleAndPlaces))
      {
         // BarkTwice()
         Console.WriteLine("Bark twice");
      }
   }

   public class WhoAndWhere
   {
      public WhoAndWhere(string name, string location)
      {
         this.Name = name;
         this.Location = location;
      }

      public string Name { get; private set; }
      public string Location { get; private set; }
   }

}
于 2009-03-03T00:43:56.100 に答える
1

私の理論では、「信号対ノイズ比」に似た有用なプログラミングの抽象化、つまり「問題とツールの比率」が存在するというものです。良さは、問題について考えるのにどれだけの時間を費やすかによって 1 つの次元で測定できます。ツールの使用方法 (この場合は言語構文) について考えるのに費やす時間と比較して、その解決策を示しています。

その手段によって、私はより少ない構造をより頻繁に使用するようにしています。なぜなら、私 (そしてできれば従う人) は、私のコード構造の本質をより迅速かつ正確に理解できるからです。そして、「for ループ」のバリエーションは、他のものが使用される可能性のあるケースを (歪みなしで) カバーするのに非常にうまく機能するため、それらが交換可能である場合、私はそれらを第一の優先事項として使用します。

また、「for」ループの先頭にある 1 行に、ループの規則について (グロクワイズに) 知っておく必要があるすべての情報が含まれていると便利です。また、同じ理由で、テストの最初に「デフォルト」スイッチを配置する傾向があります。

しかし、一貫性と明快さは最も重要な考慮事項です。もちろんYMMVです。

于 2009-02-27T17:34:27.407 に答える
1

それは、具体的な状況が何であるかに大きく依存します。しかし、あなたの例では、制限された長さの配列を歩きたいと思っています。while ループの例では、独自のインクリメントを行う必要があります。これは、ステートメントを使用して次のサイクルにスキップする場合に問題になる可能性があります。continueまた、より複雑な条件式を作成します (ところで、これにはバグがあります)。 ; 私はあなたが意味したと思います&& i != arrayLen)。for ループが提供するのに役立つ効果を達成するために、追加のコードを実行する必要があるだけです。

もちろん、一部の純粋主義者はbreakandcontinueを使用すべきではなく、ループを継続または終了するのではなく、必要に応じて if-else およびブール変数を使用する必要があると主張します。しかし、特にこの例のようにループが比較的短く、一般的に把握しやすい場合は、ループがはるかに醜く見える可能性があると思います。ブレークやコンティニューが気付かれずに簡単に隠れてしまう、はるかに長いコードを含むループの場合は、その場合ループを把握するのがすでに複雑であるため、純粋主義者のアプローチがより明確になる可能性があります。ただし、これは for ループの一部としていつでも実行できます。条件の一部として追加するだけです。

またi < arrayLen、何かが正確な値をスキップする原因となった場合に備えて、正確な等価性ではなく、バインドされた配列をテストすることiをお勧めします (私は実際に Y2K バグでこれが発生するのを見ましたが、これはより良い方法で回避できた可能性があります)。

于 2009-02-27T17:24:51.860 に答える
1

私は C++ のバックグラウンドを持っているので、「コンパイラのように考えよう」とする瞬間がまだあります。一方、ループはよりタイトなコードになる傾向があるため、for ループは、配列内のすべての要素を毎回反復処理することがわかっている場合にのみ考慮されていました。

編集: .Net を使用している場合、またはいくつかのタイトなループで VM オーバーヘッドを補うつもりがない場合、これはやり過ぎだと思います。ただし、特定のプラクティスの「理由」を覚えておくことは良いことだと思います。

于 2009-02-27T17:24:53.683 に答える
0

私の一般的なスタンスは次のとおりです。

ループカウンターがある場合は、for()を使用します(whileループのように)。

于 2009-02-27T22:15:54.640 に答える
0

while休憩はgrokabilityを減らすので、私は投票します。

ループが長くなりすぎて、実行する予定のコードを挿入しても、ループにブレークが含まれていないことに気付かない場合があります。

しかし、私はコーディングのモデルを考えさせないように購読しています。

于 2009-02-27T22:22:59.193 に答える
0

ES6 では、非常に簡単になりました。break キーワードを使用する必要はありません。find 関数を使用できます。条件が満たされると、true を返すことができます。

let barkTwice = () => {
    console.log('bark twice');
}

let arr = [{
        location: "waukee",
        name: "ter"
    }, {
        location: "milwaukee",
        name: "baxter"
    },
    {
        location: "sample",
        name: "sampename"
    }
];

ここでは条件に一致し、条件が一致すると、質問に従って関数を呼び出し、true を返します。超えないように。

arr.find(item => {
    if (item.location == "milwaukee" && item.name == "baxter") {
        barkTwice();
        return true
    }
});
于 2019-04-24T12:36:02.043 に答える