Forループなどで整数を使用する場合は、<=および> =を避ける必要がありますか?[閉まっている]


15

同等のテストはフロート変数では信頼できないが、整数では問題ないことを生徒に説明しました。私が使用している教科書では、> =と<=よりも>と<の方が読みやすいと述べています。ある程度は同意しますが、Forループでは?ループで開始値と終了値を指定する方が明確ではありませんか?

教科書の著者が正しいと思うものがありませんか?

別の例は、次のような範囲テストです。

スコア> 89グレード= 'A'の
場合スコア> 79グレード= 'B'の場合...

なぜ単に言わないのですか:スコア> = 90の場合?

loops 

11
残念ながら、これらのオプションの動作に客観的な違いはないため、これは人々がより直感的に考えるものに関する世論調査に相当し、世論調査はこのようなStackExchangeサイトには適していません。
Ixrec

1
関係ありません。本当に。
オーベロン

4
実際、これは客観的に答えられます。...でスタンド
ロバート・ハーヴェイ

9
@Ixrec「ベストプラクティス」が適切なトピックと見なされないことは常に興味深いと思います。誰がコードを改善したり、コードを読みやすくしたくないのですか?人々が同意しない場合、私たちは皆、問題のより多くの側面を学び、そして...さらに...私たちの心を変えます!うーん、それは言うのが大変でした。ロバート・ハーベイは、客観的に答えることができると言っています。これは面白いでしょう。

1
@nocomprendeほとんどの場合、「ベストプラクティス」は非常に曖昧で使い古された用語であり、言語に関する客観的な事実に基づいて有用なアドバイスを参照できますが、同じくらい頻繁に「最も人気のある意見」(またはその用語を使用している人の意見)を参照します実際には、すべてのオプションが等しく有効でも無効でもあります。この場合、ロバートのように特定のタイプのループに答えを制限し、質問に完全には答えないコメントで自分自身を指摘することによってのみ、客観的な議論を行うことができます。
Ixrec

回答:


36

ゼロベースの配列を持つカーリーブレースされたプログラミング言語では、次のforようなループを記述するのが習慣です。

for (int i = 0; i < array.Length, i++) { }

これは、配列内のすべての要素を走査し、最も一般的なケースです。<=またはの使用を回避します>=

これを変更する必要があるのは、最初または最後の要素をスキップするか、反対方向にトラバースするか、異なる開始ポイントから異なるエンドポイントに移動する必要がある場合だけです。

コレクションの場合、イテレーターをサポートする言語では、これを見るのがより一般的です:

foreach (var item in list) { }

これにより、比較が完全に回避されます。

<=vs をいつ使用するかについての厳格で速いルールを探しているなら<、それはありません。意図を最もよく表すものを使用してください。コードで「時速55マイル以下」という概念を表現する必要がある場合は、「<=ではなく」と言う必要があり<ます。

90は89ではなく実際の境界値であるため、グレード範囲に関する質問に答えるの>= 90はより理にかなっています。


9
比較を完全に回避するのではなく、列挙子の実装に移動することにより、単純に比較します。:P
メイソンウィーラー

2
もちろん、しかしそれはもはや配列を横断していません。
ロバートハーベイ

4
配列は、forこのようなループの最も一般的な使用例だからです。forここで提供したループの形式は、わずかな経験を持つ開発者であればすぐに認識できます。より具体的なシナリオに基づいて、より具体的な回答が必要な場合は、質問にそれを含める必要があります。
ロバートハーヴェイ

3
「あなたの意図を最もよく表すものを使用してください」<-これ!
ジャスパーN.ブラウワー

3
@ JasperN.Brouwerそれはプログラミングのゼロ法則のようなものです。すべてのスタイルルールとコーディング規則は、その命令がコードの明確さと矛盾する場合、最も恥ずかしく崩壊します。
Iwillnotexist Idonotexist

16

関係ありません。

しかし、議論のために、2つのオプションを分析しましょう:a > bvs a >= b

待って!それらは同等ではありません!
OK、その後、a >= b -1a > bまたはa > ba >= b +1

フム、a >bそしてa >= bよりも良好な外観の両方a >= b - 1a >= b +11とにかくこれらはすべて何ですか?そのため、ランダムs を追加または削除する必要があるため、>代わりに>=またはその逆を使用することによる利点はなくなり1ます。

しかし、それが数字の場合はどうでしょうか?それは言う方が良いですa > 7a >= 6?一瞬待って。>vs を使用し>=てハードコードされた変数を無視する方が良いかどうか真剣に議論していますか?だから、それは本当に... a > DAYS_OF_WEEKより良いかどうかの問題になりますか?それとも対ですか?そして、今回は変数名でのみsの加算/減算に戻ります。または、しきい値、制限、最大値を使用するのが最善かどうかを議論するかもしれません。a >= DAYS_OF_WEEK_MINUS_ONEa > NUMBER_OF_LEGS_IN_INSECT_PLUS_ONEa >= NUMBER_OF_LEGS_IN_INSECT1

そして、それは一般的なルールがないように見えます:それは比較されているものに依存します

しかし、実際には、コードを改善するためのはるかに重要なものと、例外を含むはるかに客観的で合理的なガイドライン(たとえば、行ごとのX文字制限)があります。


1
OK、一般的なルールはありません。(なぜ教科書が一般的なルールを述べているように思えるかもしれませんが、私はそれを手放すことができます。)それが私が逃したメモであったという新しいコンセンサスはありません。

2
@nocomprendeは、コーディングおよびフォーマットの標準が純粋に主観的であり、非常に論争的であるためです。ほとんどの場合、それらはどれも重要ではありませんが、プログラマーは依然としてそれらに対して神聖な戦争を行っています。(それらは、ずさんなコードがバグを引き起こす可能性が高い場合にのみ重要です)

1
私はコーディング標準について多くのクレイジーな議論を見てきましたが、これは簡単に理解できるかもしれません。本当に愚かな。
ジミージェームズ

@JimmyJamesは>vs >=の議論についてですか、それとも>vs の議論>=が有意義かどうかについての議論ですか?おそらくこれについて議論するのを避けるのが最善でしょう:p
タノスティンティニディス

2
「プログラムは人間が読むことを意図しており、偶然にコンピューターが実行するためだけのものです」-ドナルド・クヌース。読みやすく、理解しやすく、保守しやすいスタイルを使用してください。
-simpleuser

7

計算上使用してコストの違いはありません<>と比べ<=たりは>=。同等に高速に計算されます。

ただし、ほとんどのforループは0からカウントされます(多くの言語では配列に0インデックスが使用されているため)。したがって、これらの言語の標準的なforループは

for(int i = 0; i < length; i++){
   array[i] = //...
   //...
}

でこれを行うに<=は、オフバイワンエラーを避けるためにどこかに-1を追加する必要があります

for(int i = 1; i <= length; i++){
   array[i-1] = //...
   //...
}

または

for(int i = 0; i <= length-1; i++){
   array[i] = //...
   //...
}

もちろん、言語が1から始まるインデックスを使用する場合は、制限条件として<=を使用します。

重要なのは、条件で表現された値が問題の説明からのものであることです。読みやすい

if(x >= 10 && x < 20){

} else if(x >= 20 && x < 30){

}

半開間隔よりも

if(x >= 10 && x <= 19){

} else if(x >= 20 && x <= 29){

}

そして、19から20の間に可能な値がないことを知るために数学をしなければなりません


「長さ」の考え方はわかりますが、どこかから来た値を使用しており、開始値から終了値まで反復することを意図している場合はどうでしょう。クラスの例は、5から10のマークアップの割合でした。for (markup = 5; markup <= 10; markup ++)...?なぜテストに変更するのですか:マークアップ<11

6
@nocomprendeは、問題の説明の境界値が5と10である場合、値をわずかに変更するのではなく、コード内で明示的に指定する方がよいでしょう。
ラチェットフリーク

@nocomprende正しい形式は、上限がパラメーターである場合により明確になります for(markup = 5; markup <= MAX_MARKUP; ++markup)。他のものはそれを過度に複雑にします。
ナビン

1

ポイントは、>または> =のどちらを使用すべきかではありません。ポイントは、表現力豊かなコードを記述できるものなら何でも使用することです。

一方を追加または減算する必要がある場合は、もう一方の演算子の使用を検討してください。ドメインの良いモデルから始めると、良いことが起こることがわかります。その後、ロジックはそれ自体を書き込みます。

bool IsSpeeding(int kilometersPerHour)
{
    const int speedLimit = 90;
    return kilometersPerHour > speedLimit;
}

これは、

bool IsSpeeding(int kilometersPerHour)
{
    const int speedLimit = 90;
    return kilometersPerHour >= (speedLimit + 1);
}

他の場合には、他の方法が望ましい:

bool CanAfford(decimal price, decimal balance)
{
    return balance >= price;
}

はるかに良い

bool CanAfford(decimal price, decimal balance)
{
    const decimal epsilon = 0e-10m;
    return balance > (price - epsilon);
}

「原始的な強迫観念」を許してください。ここでは、それぞれVelocityタイプとMoneyタイプを使用したいのは明らかですが、簡潔にするために省略しました。ポイントは次のとおりです。より簡潔で、解決したいビジネス上の問題に集中できるバージョンを使用します。


2
速度制限の例は悪い例です。90km / hのゾーンで90km / hに行くことは、高速化とは見なされません。制限速度は含まれています。
-eidsonator

あなたは絶対に正しいです、私の側の脳のおなら。より良い例を使用するために自由に編集してください。うまくいけば、ポイントが完全に失われないことを願っています。
サラ

0

質問で指摘したように、フロート変数の等価性のテストは信頼できません。

とについて<=も同様です>=

ただし、整数型にはこのような信頼性の問題はありません。私の意見では、著者はどちらがより読みやすいかについて彼女の意見を表明していた。

あなたが彼に同意するかどうかは、もちろんあなたの意見です。


これは、他の著者や野外の白髪(一般的に私は白髪を持っている)によって一般的に保持されているビューですか?それが「一人のレポーターの意見」である場合、私はそれを生徒に伝えることができます。私は実際に持っています。これまでこのアイデアを聞いたことがない。

著者の性別は無関係ですが、とにかく答えを更新しました。解決しようとしている特定の問題に最も自然なものを選択する<か、それに<=基づいて選択することを好みます。他の人が指摘したように、FORループ<ではC型言語の方が理にかなっています。を好む他のユースケースがあります<=。いつでもどこでも、自由にすべてのツールを使用してください。
ダンピチェルマン

同意しない。そこできる整数型で信頼性の問題も:Cに、考慮してください。for (unsigned int i = n; i >= 0; i--)またはfor (unsigned int i = x; i <= y; i++)場合がyあることを起こりますUINT_MAX。おっと、それらは永遠にループします。
ジェームズリン

-1

関係の各<<=>=> ==及び!=2つの浮動小数点値を比較するために彼らのユースケースを持っています。それぞれに特定の意味があり、適切なものを選択する必要があります。

それぞれに正確にこの演算子が必要な場合の例を示します。(ただし、NaNに注意してください。)

  • f入力として浮動小数点値を取る高価な純関数があります。計算を高速化するために、最新の計算値のキャッシュ、つまりにマッピングxするルックアップテーブルを追加することにしましたf(x)==引数を比較するのに本当に使いたいでしょう。
  • 意味のある数で除算できるかどうかを知りたいxですか?おそらく使用したいでしょうx != 0.0
  • xが単位間隔内にあるかどうかを知りたいですか?(x >= 0.0) && (x < 1.0)正しい状態です。
  • d行列の行列式を計算し、それが正定かどうかを見たいですか?以外を使用する理由はないd > 0.0
  • あなたはΣかどうかを知りたいのn = 1、...、∞ のn 発散?テストしalpha <= 1.0ます。

浮動小数点演算(一般)は正確ではありません。しかし、それはあなたがそれを恐れ、魔法として扱い、2つの浮動小数点量が内にある場合は常に等しいとは限らないことを意味しません1.0E-10。そうすることは本当にあなたの数学を壊し、すべての奇妙なことを引き起こすでしょう。

  • たとえば、上記のキャッシュでファジー比較を使用すると、陽気なエラーが発生します。しかし、さらに悪いことに、関数はもはや純粋ではなくなり、エラーは以前に計算された結果に依存します!
  • はい、x != 0.0およびyが有限の浮動小数点値であっても、有限であるy / x必要はありません。ただしy / x、オーバーフローが原因で有限であるかどうか、または最初は操作が数学的に明確に定義されていなかったためかどうかを知ることは重要です。
  • 入力パラメーターxが単位間隔[0、1)にある必要があるという前提条件を持つ関数がある場合、x == 0.0またはで呼び出されたときにアサーションエラーが発生した場合、私は本当に動揺しx == 1.0 - 1.0E-14ます。
  • 計算した行列式は正確ではない場合があります。しかし、計算された行列式がであるときに、行列が正定値ではないふりをすると1.0E-30、何も得られません。あなたがしたことは、間違った答えをする可能性を高めることだけでした。
  • はい、あなたの引数alphaは丸め誤差の影響を受ける可能性があり、したがってalpha <= 1.0式の真の数学値alphaが実際に1より大きくても、真である可能性があります。しかし、この時点でできることはありません。

ソフトウェアエンジニアリングの場合と同様に、エラーは適切なレベルで処理し、一度だけ処理します。1.0E-10浮動小数点数を比較するたびに(これはほとんどの人が使用する魔法の値であると思われますが、理由はわかりません)の丸め誤差を追加すると、すぐに次の誤差が発生します1.0E+10


1
最も優れた答えは、確かに、尋ねられたものとは異なる質問に対してのみです。
デウィモーガン

-2

ループで使用される条件のタイプは、コンパイラが実行できる最適化の種類を、良くも悪くも制限する場合があります。たとえば、次の場合:

uint16_t n = ...;
for (uint16_t i=1; i<=n; i++)
  ...  [loop doesn't modify i]

コンパイラは、nが65535になり、iがnを超える以外の方法でループが終了しない限り、上記の条件により、n番目のパスループの後にループが終了すると想定できます。これらの条件が当てはまる場合、コンパイラは、上記の条件以外でループが終了するまでループを実行するコードを生成する必要があります。

代わりにループが次のように記述されていた場合:

uint16_t n = ...;
for (uint16_t ctr=0; ctr<n; ctr++)
{
  uint16_t i = ctr+1;
  ... [loop doesn't modify ctr]
}

コンパイラーは、ループをn回以上実行する必要がないと安全に想定できるため、より効率的なコードを生成できる可能性があります。

符号付き型のオーバーフローは、厄介な結果になる可能性があることに注意してください。与えられた:

int total=0;
int start,lim,mult; // Initialize values somehow...
for (int i=start; i<=lim; i++)
  total+=i*mult;

コンパイラは、それを次のように書き換えます。

int total=0;
int start,lim,mult; // Initialize values somehow...
int loop_top = lim*mult;
for (int i=start; i<=loop_top; i+=mult)
  total+=i;

このようなループは、計算でオーバーフローが発生しない場合は元のループと同じように動作しますが、通常、整数オーバーフローが一貫したラッピングセマンティクスを持つハードウェアプラットフォームでも永久に実行できます。


ええ、それは教科書ではもう少し先だと思います。まだ考慮されていません。コンパイラーの最適化は理解するのに役立ち、オーバーフローを回避する必要がありますが、それは私の質問の動機ではなく、人々がコードを理解する方法の観点からです。x> 5またはx> = 6の方が読みやすいですか?まあ、それは依存します

Arduino用に記述しているのでない限り、intの最大値は65535より少し高くなります。–
Corey

1
@Corey:uint16_tの最大値は、型が存在するすべてのプラットフォームで65535になります。最初の例でuint16_tを使用したのは、uint32_tの最大値よりもuint16_tの正確な最大値を知っている人が多いためです。どちらの場合も同じ点が当てはまります。2番目の例は、「int」のタイプに関して不可知論的であり、多くの人が最新のコンパイラについて認識できない重大な動作ポイントを表しています。
スーパーキャット
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.