105

我的程序中有几个循环需要。我可以写出伪代码,但我不完全确定如何在逻辑上编写它们。

我需要 -

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)是正确的吗?

第二个我不知道。我可以把它写出来if (num > 10 && num is < 21 || etc.),但必须有比这更聪明的东西。

4

13 回答 13

88

对于第一个,检查一个数字是否是使用的倍数:

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

对于第二个:

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

但这相当密集,您最好只明确列出选项。


现在您已经对自己在做什么有了更好的了解,我将第二个写成:

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

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

这是相同的逻辑,但是通过使用该函数,我们可以更清楚地了解它的含义。

于 2014-04-27T23:05:18.370 回答
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
}

但是您可能会注意到,如果您从 减去1num您将拥有以下范围:

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

换句话说,所有第一位为奇数的两位数。接下来,您需要想出一个表达这一点的公式。您可以通过除以 10 得到第一个数字,并且可以通过在除以 2 时检查余数 1 来测试它是否为奇数。将所有这些放在一起:

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

考虑到更长但可维护的代码和更短的“聪明”代码之间的权衡,我每次都会选择更长更清晰的代码。至少,如果您想变得聪明,请附上一条评论,准确解释您要完成的工作。

假设下一个处理代码的开发人员已经武装并知道你住在哪里,这会有所帮助。:-)

于 2014-04-27T23:11:06.267 回答
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;
}
于 2014-04-27T23:16:06.937 回答
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;
    });
}

为简单起见,我使用了多态 lambda (C++14) 而不是显式pair参数。这也应该坚持使用<==与标准算法保持一致,但只要为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);

这里有一个活生生的例子。

于 2014-04-27T23:24:37.100 回答
5

第一个很容易。您只需要将模运算符应用于您的 num 值:

if ( ( num % 10 ) == 0)

由于 C++ 正在评估每个不为 0 的数字为真,因此您还可以编写:

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

对于第二个,我认为这更容易理解:

该模式每 20 次重复一次,因此您可以计算模 20。除了可被 20 整除的元素之外,您想要的所有元素都将排成一行。

要获得这些,只需使用 num-1 或更好的 num+19 来避免处理负数。

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

这是假设模式永远重复,因此对于 111-120 它将再次应用,依此类推。否则,您需要将数字限制为 100:

if ( ( ( ( num + 19 ) % 20 ) > 9 ) && ( num <= 100 ) )
于 2014-04-28T00:33:28.477 回答
5

在代码中有几个很好的注释,它可以写得非常简洁和可读。

// 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 { ... }
于 2014-04-28T15:25:50.763 回答
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
}
于 2014-04-27T23:03:49.907 回答
3

这个你可能想多了。

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

第一行if (x % 10)有效,因为 (a) 是 10 的倍数的值计算为 '0',其他数字导致它们的余数,(b)if考虑 an 中的 0 值false,任何其他值都是true

编辑:

要在 20 岁左右来回切换,请使用相同的技巧。这一次,关键数字是10

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

x/10返回从 0 到 9 的任意数字 as 0, 10 到 19 as1等等。测试偶数或奇数 -& 1告诉你它是偶数还是奇数。由于您的范围实际上是“11 到 20”,因此在测试前减去 1。

于 2014-04-27T23:03:04.977 回答
1

可读性的请求

虽然您已经有了一些好的答案,但我想推荐一种编程技术,使您的代码对未来的读者更具可读性 - 可能是六个月后的您,一位要求进行代码审查的同事,您的继任者,.. .

这是为了将任何“聪明”的语句包装到一个函数中,该函数准确地(用它的名字)显示它在做什么。虽然对性能的影响很小(来自“函数调用开销”),但在这样的游戏情况下,这确实可以忽略不计。

在此过程中,您可以清理输入 - 例如,测试“非法”值。因此,您最终可能会得到这样的代码 - 看看它的可读性如何?“辅助函数”可以隐藏在某个地方(不需要在主模块中:从它们的名称可以清楚地看出它们的作用):

#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");
}
于 2014-04-29T22:32:26.773 回答
1

对于第一个:

if (x % 10 == 0)

将适用于:

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

对于第二个:

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

将申请:

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

我们基本上首先要做x-1的是:

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

然后我们将它们除以10得到:

1, 3, 5, ..

所以我们检查这个结果是否奇怪。

于 2014-04-30T06:55:42.800 回答
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
}
于 2014-04-30T19:30:21.293 回答
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陈述的目的)......

于 2014-04-30T01:39:35.380 回答
0

我知道这个问题有很多答案,但无论如何我都会把我的问题扔在这里......

摘自 Steve McConnell 的Code Complete,第 2 版:“阶梯访问表:

还有一种表访问方式是阶梯法。这种访问方式不像索引结构那样直接,但也不会浪费太多的数据空间。如图 18-5 所示,阶梯结构的一般思想是表中的条目对数据范围有效,而不是对不同的数据点有效。

在此处输入图像描述

图 18-5 阶梯式方法通过确定每个条目到达“阶梯”的级别来对其进行分类。它击中的“步骤”决定了它的类别。

例如,如果您正在编写评分程序,“B”条目范围可能是 75% 到 90%。以下是您有一天可能需要编程的一系列成绩:

在此处输入图像描述

要使用阶梯法,您将每个范围的上端放入一个表中,然后编写一个循环来检查每个范围的上端的分数。当您找到分数首次超过范围顶部的点时,您就知道成绩是多少。使用阶梯技术,您必须小心正确处理范围的端点。下面是 Visual Basic 中的代码,它根据此示例为一组学生分配成绩:

在此处输入图像描述

虽然这是一个简单的示例,但您可以轻松地将其推广到处理多个学生、多个评分方案(例如,不同作业的不同分数级别的不同成绩)以及评分方案的变化。”

Code Complete,第 2 版,第 426 - 428 页(第 18 章)。

于 2014-04-30T17:27:26.570 回答