数値が10の倍数であるか、特定の範囲のセット内であるかを判断する


103

プログラムに必要なループがいくつかあります。疑似コードを書き出すことはできますが、論理的にどのように書くかは完全にはわかりません。

私は欲しい -

if (num is a multiple of 10) { do this }

if (num is within 11-20, 31-40, 51-60, 71-80, 91-100) { do this }
else { do this } //this part is for 1-10, 21-30, 41-50, 61-70, 81-90

これは、ヘビとはしごのボードゲーム用です。

モジュラスを使用する必要がある最初のifステートメントif (num == 100%10)が正しいと思いますか?

2番目はわかりません。私はそれを書き出すことができますが、それif (num > 10 && num is < 21 || etc)よりも賢いものが必要です。


16
一般的に、良いコードの長さは、それが何をするかを説明する英語の長さに比例します。したがって、「仕様」が11-20、31-40、51-60、71-80、91-100と言っている場合、コードがそれらの数値も言及することを期待できます。これらの数値がどこかからのものであるか、何らかの理由で生成された場合は、数値ではなく理由をコーディングできるかどうかを確認してください。
ルキ2014

39
@ user3419168:コンパイラは、コードがどれほど読みやすいかを気にしません。ほんの一瞬でコンパイルされます。しかし、あなたのコードを読む人間にとって、あなたが行う選択は、コードを数秒、数分、数時間で理解することもあれば、まったく理解しないこともあります。これにはコストがかかります。人々はコードを読んで理解することで報酬を得ているので、彼らにそれを簡単にさせてください。読みやすさを最大化するために常にプロダクションコードを記述します。簡潔にすることで、必ずしもコードのパフォーマンスが向上するわけではないことに注意してください。
Eric Lippert、2014

2
@AmadeusDrZaius-私はほとんど同じことをしていませんが、パフォーマンスの重要なセクションに対してのみです。1億回呼び出される最もタイトなループは適格です-ヘビとはしごのゲームのifステートメントはそうではありません。それらの間に線を引く場所は個人的な選択です。
フロリス14

2
私はそれを言うのが嫌ですが、初心者が実際のコードを書くのに十分な企業作業を行ったので、私はそれを力ずくで強制することをお勧めする必要があります。なぜなら、新しい人たちはそれを理解し、壊さないからです。悲しいが本当-場合によっては、ばかげているのが賢明です。
Richard Le Mesurier 2014

22
これはまともな質問です。ポスターから何も取り上げたくないのですが、500ポイント以上の価値はありません。これが、何千ものポイントを持つ人々がここで権威であるように見えることで私たちがするナンセンスのいくつかに終わる方法です。(このコメントが別の場所にある場合は、自由に移動してください。)
GaTechThomas 14年

回答:


86

最初の例では、数値が使用の倍数であるかどうかを確認するには:

if (num % 10 == 0) // It's divisible by 10

2番目の場合:

if(((num - 1) / 10) % 2 == 1 && num <= 100)

しかし、それはかなり密度が高く、オプションを明示的にリストするだけの方がよい場合があります。


あなたはあなたが何をしているのかについてより良い考えを与えたので、私は2番目のものを次のように書きます:

   int getRow(int num) {
      return (num - 1) / 10;
   }

   if (getRow(num) % 2 == 0) {
   }

それは同じロジックですが、関数を使用することで、それが何を意味するのかをより明確に理解できます。


79
if((num - 1) / 10) % 2 == 1 && num < 100)-それを見たら泣いてしまいます。
Daniel Kamil Kozar 14

32
@DanielKamilKozar、あなたがすべきです。
Winston Ewert 2014

2
@ user3419168、それだけでは、それが世界で何を意味するのか疑問に思う人がいます。それが世界で何をしようとしているのかについてのヒントはありません。編集で、ロジックを関数に分割して、実際に計算が何を行っているかを明確にするバージョンを示したのはこのためです。
Winston Ewert 2014

3
num >= 11(1)下限が禁止されていること、および(2)%負の数で負の数も返されることを主張することも賢明です。(& 1ここでの使用は「安全」であることを認めなければなりませんが、追加の知識も前提としています。)
usr2564301 '27

2
編集の+1は、範囲のリストの理由を取得し、読み取り可能な方法で表示します。IMO、1つ上のステップgetRow(num) % 2 == 0は、関数をラップして、意図を明確にすることです。bool inEvenRow(int num){ return getRow(num) % 2 ==0;}
Mr.Mindor

40

if(numは10の倍数){これを行う}

if (num % 10 == 0) {
  // Do something
}

if(numが11-20、31-40、51-60、71-80、91-100の範囲内){これを実行}

ここでの秘訣は、範囲間で何らかの共通性を探すことです。もちろん、「ブルートフォース」メソッドはいつでも使用できます。

if ((num > 10 && num <= 20) ||
    (num > 30 && num <= 40) ||
    (num > 50 && num <= 60) ||
    (num > 70 && num <= 80) ||
    (num > 90 && num <= 100)) {
  // Do something
}

しかし、気づくかもしれませんが、1から減算するnumと、範囲が得られます。

10-19, 30-39, 50-59, 70-79, 90-99

つまり、最初の桁が奇数であるすべての2桁の数字。次に、これを表す式を考え出す必要があります。10で除算することで最初の数字を取得でき、2で除算したときに残りの1をチェックすることで奇数であることをテストできます。

if ((num > 0) && (num <= 100) && (((num - 1) / 10) % 2 == 1)) {
  // Do something
}

長くても保守しやすいコードと短い「賢い」コードとの間のトレードオフを考えると、毎回より長く明確なコードを選択します。少なくとも、賢くなろうとするなら、達成しようとしていることを正確に説明するコメントを含めてください。

コードに取り組む次の開発者が武装していて、あなたがどこに住んでいるのかを知っていると想定すると役立ちます。:-)


7
私はまだ賢いコードを選びますが、関数を抽出することでそれを保守可能なコードに変えます。最後の部分が言った場合と同じように読みやすくなるでしょう&& isTensDigitOdd(num)。おそらく、関数定義の前に、それが何をするのかを説明するコメントが付いています。そのようなパターンが存在する場合、そのパターンの理由を説明するコメントは、保守容易性のために啓発的です。
クリス14

3
クリス、それは「賢さ」に明らかな利点がある場合の優れた戦略です。コードがはるかに短い(つまり、タイプミスの可能性が低く、特に変更された場合)、または効率が大幅に向上します。ほとんどの場合、簡潔さ、明快さ、および効率の間にはトレードオフがあり、適切な妥協点を見つけることは、開発するための優れたスキルです。(スニッカーについては、stackoverflow.com / a / 2151844/29157を参照してください。)
Adam Liss

1
これははるかに優れたアプローチです。「賢いコード」よりもはるかに理解しやすく、パフォーマンスの違いはごくわずかです。
user1477388 14

@AdamLiss、はい、私はこれらの決定の影響を確認するのに十分な経験がなかったので、私の意見はほとんど価値がありません。私はすぐに十分だと確信しており、必要に応じてセカンドオピニオンを必ず取得します。
クリス14

1
空売りしないでください。あなたの本能は非常に賢明であり、あなたは学び続けることを切望しています。正当な理由がある場合はすべての意見が価値があります...場合によっては、そうでない場合もあります。私はあなたが遠くまで行くお金を賭けるでしょう。
Adam Liss 14

30

GCCまたは大文字小文字の範囲をサポートするコンパイラーを使用している場合はこれを行うことができますが、コードは移植できません

switch(num)
{
case 11 ... 20:
case 31 ... 40:
case 51 ... 60:
case 71 ... 80:
case 91 ... 100:
    // Do something
    break;
default:
    // Do something else
    break;
}

1
このコードが移植できない理由を教えてください。
M Sharath Hegde 14

8
@MSharathHegdeは、GCC拡張が必要ですが、これは標準の一部ではなく、一部のコンパイラではサポートされません
Bryan Chen

5
これは正解です。意図が何であるかがすぐにわかるからです。コメントがあっても、moduloを使用した「賢い」答えはすべて、メンテナンスの悪夢です。
smirkingman 2014

@smirkingman確かにそれは私がメインの質問への私のコメントで言ったことです。企業の仕事で新しいコーダーの経験を積むだけで、スマートニンジャよりも明白な方法の方が優れていることがよくあります。
Richard Le Mesurier 2014

15

これは初心者というよりも将来の訪問者のためのものです。より一般的なアルゴリズムのような解決策として、開始値と終了値のリストを取得して、渡された値がそれらのいずれかに含まれているかどうかを確認できます。

template<typename It, typename Elem>
bool in_any_interval(It first, It last, const Elem &val) {
    return std::any_of(first, last, [&val](const auto &p) {
        return p.first <= val && val <= p.second;
    });
}

簡単にするために、明示的なpair引数の代わりに多相ラムダ(C ++ 14)を使用しました。これはおそらく、標準アルゴリズムの使用<==一貫性を保つ必要がありますが、定義されElemている限り、このように機能<=します。とにかく、それはこのように使用できます:

std::pair<int, int> intervals[]{
    {11, 20}, {31, 40}, {51, 60}, {71, 80}, {91, 100}
};

const int num = 15;
std::cout << in_any_interval(std::begin(intervals), std::end(intervals), num);

ここにライブの例があります


きちんとしたソリューション。ペアを表すために1行あたり2つの数値でフォーマットできるので、おそらく単一の配列を使用したでしょう。
Kevin Lam 14

@ HunterGuy2、非常に良い点。ペアで動作するように実際に変更するのは、なぜかzipイテレータのみを考えていたからです。
クリス14

本当に素晴らしいstlアプローチ!大好きです!
higuaro 2014

5

最初のものは簡単です。あなたはあなたのnum値にモジュロ演算子を適用する必要があるだけです:

if ( ( num % 10 ) == 0)

C ++は0ではないすべての数値をtrueとして評価しているので、次のように書くこともできます。

if ( ! ( num % 10 ) )  // Does not have a residue when divided by 10

2番目のものについては、これを理解するのがよりクリーンだと思います:

パターンは20ごとに繰り返されるため、20を法として計算できます。20で割り切れる要素を除いて、必要な要素はすべて1行になります。

それらも取得するには、負の数を扱わないように、num-1以上のnum + 19を使用します。

if ( ( ( num + 19 ) % 20 ) > 9 )

これは、パターンが永久に繰り返されることを想定しているため、111〜120の場合は再び適用されます。それ以外の場合は、数を100に制限する必要があります。

if ( ( ( ( num + 19 ) % 20 ) > 9 ) && ( num <= 100 ) )

5

コードに2、3の適切なコメントがあるため、非常に簡潔で読みやすく記述できます。

// Check if it's a multiple of 10
if (num % 10 == 0) { ... }

// Check for whether tens digit is zero or even (1-10, 21-30, ...)
if ((num / 10) % 2 == 0) { ... }
else { ... }

2
最初のコメントは不要です。少し経験を持つ任意のプログラマはそれが知っているだろうnum % 10 == 0と同じことであるnum10の倍数である
ジャスティン

7
はい、しかし初心者もこのサイトを読みます。私は通常、自分のコードでそのコメントを使用しませんが、この初心者の質問から恩恵を受ける初心者には、答えが明確になります。
La-comadreja 14

2
これをしないでください。リーダーの速度を低下させ、すべてを2回読み取ることを強制することにより、実際には読みやすさが低下します。それを理解していないプログラマはコードを保守するべきではないのとif (num % 10 == 0)同じことを意味// Check if it's a multiple of 10ます。これはよく知られたアンチパターンです。
Dawood ibnカリーム

1
@DavidWallace上記のコメントを参照してください。この投稿の読者がこのアンチパターンを知っていることを保証することはできません。
La-comadreja 2014年

1
いいえ、各行にコメントしてそれが何をするかを言うのはアンチパターンです。使用%がアンチパターンであることを意味するものではありません。明らかにそうではありません。本当に、この投稿の読者の多くが初心者であると仮定して、このようなコメントの書き方を彼らに教えることは、プログラマーとしての彼らの発展に否定的な貢献をしています。
Dawood ibnカリーム2014年

4

基本的に自分で答えを説明しましたが、念のためコードを示します。

if((x % 10) == 0) {
  // Do this
}
if((x > 10 && x < 21) || (x > 30 && x < 41) || (x > 50 && x < 61) || (x > 70 && x < 81) || (x > 90 && x < 101)) {
  // Do this
}

2
x < 41 x > 50括弧を訂正して入れてください。
101010 14

1
@ 40twoは、技術的にoperator&&はよりも優先順位が高いoperator||ため、問題ありませんが、GCCが警告を表示することは間違いありません。
クリス14

18
ではなく10 < x < 21として不等式を表すことを検討してください。数学的に書くのと同じ順序であると、不等式を読みやすくなります。10 < x && x < 21x > 10 && x < 21
Eric Lippert 14

5
このコードはかなり読みにくく、実際のロジックについてはほとんど触れていません。この答えは嫌いです。
Dariusz 14

3
OPが何をしたか正確に答えたので、私はこれに反対票を投じています。
ブルーノフェレイラ

3

あなたはこれを考えすぎているかもしれません。

if (x % 10)
{
   .. code for 1..9 ..
} else
{
   .. code for 0, 10, 20 etc.
}

最初の行 if (x % 10)機能するのは、(a)10の倍数である値は「0」として計算され、他の数値は余りとなるため、(b)の0の値ifは考慮されるfalseため、他の値はtrueです。

編集:

20代で前後に切り替えるには、同じトリックを使用します。今回、重要な番号は10次のとおりです。

if (((x-1)/10) & 1)
{
  .. code for 10, 30, ..
} else
{
   .. code for 20, 40, etc.
}

x/100から9までの任意の数値をとして返し0、10から19 までを返します1。偶数または奇数でテストすると、偶数か奇数& 1かがわかります。実際の範囲は「11から20」なので、テストする前に1を引きます。


1

読みやすさのお願い

良い答えはすでにいくつかありますが、将来の読者がコードを読みやすくするプログラミング手法をお勧めします。これは、6か月後にはあなたになる可能性があるため、同僚がコードレビューの実行を依頼しました。 。

これは、「賢い」ステートメントを関数にラップし、関数の名前を(正確に)示すためです。(「関数呼び出しのオーバーヘッド」による)パフォーマンスへの影響はごくわずかですが、このようなゲームの状況では、これは本当に無視できます。

途中で入力を無害化できます-たとえば、「不正な」値をテストします。したがって、あなたはこのようなコードで終わるかもしれません-それがどれほど読みやすいか見てください?「ヘルパー関数」はどこかに隠しておくことができます(メインモジュールにある必要はありません。名前からわかるとおり)。

#include <stdio.h>

enum {NO, YES, WINNER};
enum {OUT_OF_RANGE=-1, ODD, EVEN};

int notInRange(int square) {
  return(square < 1 || square > 100)?YES:NO;
}

int isEndOfRow(int square) {
  if (notInRange(square)) return OUT_OF_RANGE;
  if (square == 100) return WINNER; // I am making this up...
  return (square % 10 == 0)? YES:NO;
}

int rowType(unsigned int square) {
  // return 1 if square is in odd row (going to the right)
  // and 0 if square is in even row (going to the left)
  if (notInRange(square)) return OUT_OF_RANGE; // trap this error
  int rowNum = (square - 1) / 10;
  return (rowNum % 2 == 0) ? ODD:EVEN; // return 0 (ODD) for 1-10, 21-30 etc.
                                       // and 1 (EVEN) for 11-20, 31-40, ...
}

int main(void) {
  int a = 12;
  int rt;
  rt = rowType(a); // this replaces your obscure if statement

  // and here is how you handle the possible return values:
  switch(rt) {
  case ODD:
    printf("It is an odd row\n");
    break;
  case EVEN:
    printf("It is an even row\n");
    break;
  case OUT_OF_RANGE:
    printf("It is out of range\n");
    break;
  default:
    printf("Unexpected return value from rowType!\n");
  }

  if(isEndOfRow(10)==YES) printf("10 is at the end of a row\n");
  if(isEndOfRow(100)==WINNER) printf("We have a winner!\n");
}

3
それは、とあまりにも遠く、それをプッシュしようとしていないYESNO
rmobis 2014

@Raphael_-それはよくあるかもしれません:私は単に「例えば」を示していました。多くの人が真/偽を使用しています。しかし、(異なる言語が異なる規則を使用しているため)、私が覚えていることはありません:されているTRUETrueまたはtrue?そして、もしあれば、通常のCに含める必要があるヘッダーファイルは何ですか?だから私は自分で転がした。それが反対票だったのかどうか疑問に思います...
フローリス14

1

最初のものについて:

if (x % 10 == 0)

に適用されます:

10, 20, 30, .. 100 .. 1000 ...

2番目の場合:

if (((x-1) / 10) % 2 == 1)

に適用されます:

11-20, 31-40, 51-60, ..

基本的に、最初に次のことx-1を行います。

10-19, 30-39, 50-59, ..

次に、それらを分割し10て取得します。

1, 3, 5, ..

したがって、この結果が奇数かどうかを確認します。


1

以下を試すことができます:

        // multiple of 10
        if ((num % 10) == 0)
        {
           // Do something
        }
        else if (((num / 10) % 2) != 0)
        {
            //11-20, 31-40, 51-60, 71-80, 91-100
        }
         else
        {
            //other case
        }

OPの質問では、10の倍数のチェックは範囲チェックとは関係ありません。範囲チェックでは、コード((20/10)%2)->( 2%2)-> 0
セルピトン2014

0

この質問にはたくさんの答えがあることは知っていますが、とにかくここに投げます...

Steve McConnellのコード完了から取得、第2版: "階段アクセステーブル:

さらに別の種類のテーブルアクセスは、階段方式です。このアクセス方法は、インデックス構造ほど直接的ではありませんが、データ領域を無駄にすることはありません。図18-5に示す階段構造の一般的な考え方は、テーブルのエントリは、個別のデータポイントではなく、データの範囲に対して有効であるということです。

ここに画像の説明を入力してください

図18-5階段アプローチでは、「階段」に到達するレベルを決定することにより、各エントリを分類します。ヒットする「ステップ」によって、そのカテゴリが決まります。

たとえば、評価プログラムを作成している場合、「B」エントリの範囲は75%から90%になる可能性があります。いつかプログラムしなければならない可能性がある成績の範囲は次のとおりです。

ここに画像の説明を入力してください

stair-stepメソッドを使用するには、各範囲の上限をテーブルに入れ、各範囲の上限に対するスコアをチェックするループを記述します。スコアが最初に範囲の上限を超えるポイントを見つけると、グレードがわかります。階段法では、範囲の端点を適切に処理するように注意する必要があります。この例に基づいて学生のグループに成績を割り当てるVisual Basicのコードは次のとおりです。

ここに画像の説明を入力してください

これは簡単な例ですが、簡単に一般化して、複数の学生、複数の採点スキーマ(たとえば、異なる課題の異なるポイントレベルの異なる評点)、および採点スキーマの変更を処理できます。」

コードコンプリート、第2版、ページ426-428(第18章)。


なぜこれが間違った質問だと思いますか?OPケースの例を提供しなかったからといって、間違った質問に答えたことを意味するわけではありません...
lauCosma

0

他の人が指摘したように、条件をより簡潔にすることは、コンパイルや実行の速度を上げることにはならず、必ずしも読みやすさを向上させることにもなりません。

後で6 x 6のボードで幼児バージョンのゲームが必要になった場合や、40 x 50のボードで高度なバージョン(一晩中プレイできる)が必要になった場合に、プログラムをより柔軟にするのに役立ちます。 。

だから私はそれを次のようにコーディングします:

// What is the size of the game board?
#define ROWS            10
#define COLUMNS         10

// The numbers of the squares go from 1 (bottom-left) to (ROWS * COLUMNS)
// (top-left if ROWS is even, or top-right if ROWS is odd)
#define firstSquare     1
#define lastSquare      (ROWS * COLUMNS)
// We haven't started until we roll the die and move onto the first square,
// so there is an imaginary 'square zero'
#define notStarted(num) (num == 0)
// and we only win when we land exactly on the last square
#define finished(num)   (num == lastSquare)
#define overShot(num)   (num > lastSquare)

// We will number our rows from 1 to ROWS, and our columns from 1 to COLUMNS
// (apologies to C fanatics who believe the world should be zero-based, which would
//  have simplified these expressions)
#define getRow(num)   (((num - 1) / COLUMNS) + 1)
#define getCol(num)   (((num - 1) % COLUMNS) + 1)

// What direction are we moving in?
// On rows 1, 3, 5, etc. we go from left to right
#define isLeftToRightRow(num)    ((getRow(num) % 2) == 1)
// On rows 2, 4, 6, etc. we go from right to left
#define isRightToLeftRow(num)    ((getRow(num) % 2) == 0)

// Are we on the last square in the row?
#define isLastInRow(num)    (getCol(num) == COLUMNS)

// And finally we can get onto the code

if (notStarted(mySquare))
{
  // Some code for when we haven't got our piece on the board yet
}
else
{
  if (isLastInRow(mySquare))
  {
    // Some code for when we're on the last square in a row
  }


  if (isRightToLeftRow(mySquare))
  {
    // Some code for when we're travelling from right to left
  }
  else
  {
    // Some code for when we're travelling from left to right
  }
}

はい、それは冗長ですが、ゲームボードで何が起こっているかを正確に明らかにします。

このゲームを開発して携帯電話やタブレットで表示する場合は、定数の代わりにROWS変数とCOLUMNS変数を作成して、画面のサイズと向きに合わせて(ゲームの開始時に)動的に設定できるようにします。

ゲームの途中でいつでも画面の向きを変更できるようにしたい-あなたがする必要があるのは、ROWSとCOLUMNSの値を入れ替えるだけで、他はすべて残したままにする(各プレーヤーが現在オンになっている四角の数字、そしてすべてのヘビとはしごの正方形の開始/終了)は変更されません。次に、ボードをきれいに描き、アニメーションのコードを書くだけです(私はそれがあなたの目的だったと思います)ifステートメントの)...


私もフローリスの回答に賛成票を入れました。同じような結果を達成するための別のスタイルです。回答を書く前には見られませんでした
Laurence Renshaw

2
代わりにインライン関数を使用する必要があります#define
Bryan Chen

関数のような#define命令を使用するときは、展開内の引数を括弧で囲んでください。だから#define finished(num) (num == lastSquare)あなたの代わりに書くべき#define finished(num) ((num) == lastSquare)です。その理由は、優先順位が十分に低い演算子を含む式でそのような命令を使用すると、期待する答えが得られないためです。この場合、追加の括弧を使用しないと、ほぼ間違いなく、にfinished(a & b)展開され(a & b == lastSquare)ます。
Dawood ibnカリーム2014年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.