追跡し、ループのバグを回避するためのヒントは何ですか?
-
06-07-2019 - |
質問
見つけました...もう一度...次のようなリアルタイムの浪費バグ
for (int i = 0; i < length; i++)
{ //...Lots of code
for (int j = 0; i < length; j++)
{
//...Lots of code
}
}
jにすべきSHOULD BE私もそうしなかった。だからこれから私は使うつもりだ:
for (int i = 0; i < length; i++)
{
for (int i1 = 0; i1 < length; i1++)
{
}
}
内部および外部のwhileおよびforループのヒントは何ですか?
編集:貴重な回答をありがとう。提案されたヒントの簡単な要約:
- インデックス変数に意味のある変数名を使用します(代わりにSomeObjCollectionLengthを使用します)
- 内側のループの内容を別のメソッドに配置し、そのメソッドを外側のループから呼び出します
- 外側のループと内側のループの間のコードの行数が管理できないため、コードの匂いが強くなります
- コピーと貼り付けを避け、注意してインデックス変数を書きます
- 可能な限りforeachとイテレータを使用する
- ループに入る直前に変数を初期化する
- 各ループで1つの機能のみを実行します。単一のループに責任を混在させることを避けます
- 可能であれば、一度にすべてを表示できるようにループを短くします
解決
i&amp;を使用しないj(または他の任意の1文字の変数)をインデックス名として。適切な名前を使用すると、この種の問題に陥ることはありません。
他のヒント
最も単純でクリーンな解決策の1つは、次のようになるように内部ループの内容をメソッドに配置することです。
for (int i = 0; i < length; i++)
{
DoSomething();
}
private void DoSomething(int outerValue)
{
for (int i = 0; i < length; i++)
{
// Do something else
}
}
私にとって、ここでの「コードの匂い」とは「大量のコード」です。
ループ内のコードの量が特に大きい場合、内側と外側のループ間の距離は、正確性のために互いに比較される可能性が低いことを意味します。
確かに、内側のループの開始点を単独で見ると、問題に注意を払う必要がありますが、可能な限りコードの小さなセクションにメイン構造があると、脳の消化が少なくなります。
メイン構造のサイズを小さくするために、「コードのロット」セクションを個別の関数/メソッドに抽出することは可能かもしれませんが、これは常に実用的ではありません。
また、「i2」や「i3」などを促進する傾向があるため、「i1」は特に適切な変数名の選択とは言えません。すべてのループ変数をより意味のあるものに置き換えると、コードがわかりやすくなり、元のエラーが発生する可能性が低くなります。
より良いループコードを書くための私の最高のアドバイス(順不同)(この多くは優れた本コード完了):
- ループの複数の出口点を避けます。
- continue / breakは控えめに使用してください。
- 可能な場合、ネストされたループを個別のルーチンにリファクタリングします。
- 意味のある変数名を使用して、ネストされたループを読み取り可能にします。
- for(i = ...)ループではなく、可能であればforeach()ループを使用します。
- 1つの場所からのみループに入ります。 gotoでループにジャンプしないでください。今まで。
- ループの直前に初期化コードを挿入します。
- ループ初期化ステートメントを、関連するループとともに保持します。
- ネストされていないループ間で変数を再利用しないでください。 10.ループインデックス変数のスコープをループ自体に制限します。
- for(;;)ではなく、while(true)を無限ループに使用します
- ブロック構造を提供する言語(例: '{'および '}')では、ループのステートメントを囲むためにインデントするのではなく、それらを使用します。はい、1行のループでも。
- 空のループを避けます。
- ハウスキーピング作業をループの中央に配置するのを避け、代わりに先頭または末尾に配置します。
- 各ループで1つの機能のみを実行します。単一のループで責任を混在させることは避けてください。
- ループの終了条件を明確にします。
- for()ループのループインデックス変数を使用して終了させないでください。
- ループインデクサーの最終値に依存するコードを避けます。
- 複雑なループで安全カウンターを使用することを検討してください-ループの実行回数が多すぎたり、少なすぎたりしないようにチェックできます。
- 可能であれば、break文を使用してwhileループを終了します。
- 可能であれば、一度にすべてを表示できるようにループを短くします。
これはコピーと貼り付けの間違いです。コピーと貼り付けは避けてください。
ソリューションに関しては、それほど良くありません。間違いはまだコードのトンの間で滑ることができます。ループ一時変数にも意味のある名前を使用する傾向があります。
IDEを活用し、VSでこれを使用してみてください: http://msdn.microsoft.com/en-us/library/z4c5cc9b(VS.80).aspx
サンプル:「 for 」と入力し、続いて Tab Tab を押します
私はここに来て賢くなり、「初めて書きます」と言います。しかし、その後、私はあなたの例を見て、まあ、私はそれを何度も自分でやりました。
そのようなネストされたループが必要な場合、私の唯一の解決策は、コードを書くときに注意を払って考えることです。
可能な場合、イテレータとfor eachループを使用すると便利です。
また、提案された解決策がどのように改善されるかわかりません。また、見た目も良くありません。
まず、ループ本体のサイズを小さくします。つまり、別の機能に移動します。通常、画面に収まるよりも長い関数を使用することはお勧めできません。そのため、ループはさらに小さくする必要があります。
次に、このような場合に意味のある変数名を使用します。私は、数行のコードを持つ単純なループでのみiとjを使用します。たとえば、2次元配列を使用している場合、&quot; col&quot;および「行」より理解しやすく、コードを読みやすく(「どちらがどちらですか?」)、このような間違いを見つけやすくします。
このような問題に特に注意する必要がありますが、これに対する魔法の弾丸はありません。 「より良いネーミング」でもネストされたループのN番目または(N + M)番目のレベルであるかどうかをたまに失い、エラーを起こすことを時々提案します。
ネストされたループが必要な場合は、慎重に記述してください。インデックスの誤用に対する適切なガードとなる関数に外側のループ本体を抽出することで回避できる場合。
本当に必要な場合は、一時ループカウンターに「ii」と「jj」を使用します。「i」と「j」よりも検索しやすく、上記の例で簡単に見つけることができます。より良くするために、実際の変数名を実際に使用できます。文字列をループしている場合、characterIndexまたは何かと呼ぶことができます。より多くの入力ができますが、ドキュメント化され、後で不明瞭な問題をデバッグする時間を節約できます。
さらに良いのは、数値カウンターを避け、コレクションに対して名前付きイテレーターを使用することです。私の意見では、彼らは意図をより明確にします。
最後に、可能であれば、ループを完全に廃止することをお勧めします: Boost :: Foreach はC ++でこれを行う方法の1つですが、一般的には、インデックス値をインクリメントしたりコンテナのコンテンツを直接反復したりする必要のないPythonなどの言語を使用することを好みますイテレータ。
より宣言的なループ構造を使用してみてください。たとえば、実際にインデックス( i
sおよび j
s)が必要でなく、プログラミング環境で許可されている場合、 foreach
を構築して、コレクションを反復処理します。
多くの場合と同様に、Steve McConnellの Code Complete には優れたアドバイスがあります。優れたループコードの構築について彼が言っていることを読むのは、時間をかけるだけの価値があるでしょう。ここには本のコピーは手元にありませんが、本全体を読む価値はあります。