确定一个数字是十的倍数还是在一组特定的范围内
我在我的程序中需要一些循环。 我可以写出伪代码,但我不完全知道如何写他们的逻辑。
我需要 –
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)
但必须有比这更聪明的东西。
对于第一个,检查一个数字是否是一个倍数的使用:
if (num % 10 == 0) // its divisible by 10
对于第二个:
if(((num - 1) / 10) % 2 == 1 && num <= 100)
但是,这是相当密集的,你可能会更好的明确列出的选项。
现在你已经更好地了解了你在做什么,我会写第二个:
int getRow(int num) { return (num - 1) / 10; } if (getRow(num) % 2 == 0) { }
它的逻辑是相同的,但是通过使用这个函数,我们可以更清楚地知道它的含义。
如果(num是10的倍数){做这个}
if (num % 10 == 0) { // Do something }
如果(数字在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 }
但是你可能会注意到,如果你从num
减去1
,你会得到范围:
10-19, 30-39, 50-59, 70-79, 90-99
换句话说,所有的2位数字的第一个数字是奇数。 接下来,你需要拿出一个expression这个的公式。 你可以通过除以10得到第一个数字,并且当除以2时检查余数为1,可以testing它是奇数。把所有这些放在一起:
if ((num > 0) && (num <= 100) && (((num - 1) / 10) % 2 == 1)) { // Do something }
考虑到较长的代码和较短的“巧妙”代码之间的权衡,每次我都会select更长更清晰的代码。 至less,如果你想要聪明,请包括一个评论,说明你正在努力完成什么。
这有助于假定下一个开发者在代码上工作并知道你的位置。 🙂
如果您正在使用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; }
这对于未来的访问者来说比初学者更重要。 对于一个更一般的algorithm类解决scheme,您可以获取开始和结束值的列表,并检查传递的值是否在其中之一:
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
参数。 这也应该坚持使用<
和==
与标准algorithm保持一致,但只要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);
这里有一个生动的例子。
在代码中有一些好的评论,它可以写得相当简洁和可读。
// 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 { ... }
第一个很容易,你只需要将模运算符应用到你的num值:
if ( ( num % 10 ) == 0)
SInce c ++正在评估每个不是0的数字,你也可以这样写:
if ( ! ( num % 10 ) ) //does not have a residue when devided by 10
对于第二个,我认为这是更清晰的理解:
这个模式每20个重复一次,所以你可以计算模数20.你想要的所有元素都是连续的,除了那些可以被20除的元素。
为了得到这些,只需使用num-1或更好的num + 19来避免处理负数。
if ( ( ( num + 19 ) % 20 ) > 9 )
这是假设模式永远重复,所以111-120它将再次适用,等等。 否则,您需要将数字限制为100:
if ( ( ( ( num + 19 ) % 20 ) > 9 ) && ( num <= 100 ) )
你基本上自己解释了答案,但这里的代码以防万一。
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 }
你可能会过度的这个。
if (x % 10) { .. code for 1..9 .. } else { .. code for 0, 10, 20 etc. }
第一行if (x % 10)
工作是因为(a)10的倍数的值计算为“0”,其他数字导致它们的剩余,(b) if
的值被认为是false
,其他值是true
。
编辑:
要在二十年代来回切换,请使用相同的技巧。 这一次,关键数字是10
:
if (((x-1)/10) & 1) { .. code for 10, 30, .. } else { .. code for 20, 40, etc. }
x/10
返回从0到9的任何数字作为0
到19作为1
,依此类推。 testing偶数或奇数 – & 1
– 告诉你是偶数还是奇数。 由于你的范围实际上是“11到20”,testing前减1。
恳求可读性
虽然你已经有了一些很好的答案,但是我想推荐一种编程技术,使得你的代码对于未来的读者更具可读性 – 在六个月内,你可以是一个同事,一个要求执行代码审查的同事,你的继任者。 。
这是将任何“聪明”的语句包装到一个函数中,该函数完全显示(使用它的名字)它在做什么。 虽然对性能的影响很小(来自“函数调用开销”),但在这种游戏情况下,这是微不足道的。
一路上,你可以消毒你的input – 例如,testing“非法”的价值。 因此,你可能会得到这样的代码 – 看看它是多么可读? “辅助函数”可以隐藏在某个地方(不需要在主模块中:从他们的名字中可以清楚地看到他们的工作):
#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"); }
对于第一个:
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, ..
所以我们检查一下这个结果是不是很奇怪
您可以尝试以下方法:
// 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 }
正如其他人所指出的,使条件更简洁不会加速编辑或执行,也不一定有助于可读性。
在哪里可以提供帮助的是让程序更加灵活,如果你稍后决定要在6 x 6的棋盘上使用幼儿的游戏版本,或者需要一个高级的版本(可以玩一整夜) 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和COLUMNSvariables而不是常量,因此可以dynamic设置它们(在游戏开始时),以匹配屏幕大小和方向。 我还允许在游戏中期随时更改屏幕方向 – 您只需切换ROWS和COLUMNS的值,而将所有其他值(每个玩家所在的当前方块数)和所有蛇和梯子的开始/结束方格)不变。 然后,你只需要很好地绘制棋盘,并为你的animation编写代码(我认为这是你的if
语句的目的)…
我知道这个问题有这么多的答案,但是我仍然会把它扔到这里…取自史蒂夫·麦康奈尔的Code Complete 2nd Edition:“Stair-Step Access Tables:
还有另一种表格访问是楼梯的方法。 这种访问方法并不像索引结构那样直接,但是不会浪费太多的数据空间。 图18-5所示的阶梯结构的总体思路是,表中的条目对于数据范围而不是对于不同的数据点有效。
图18-5阶梯式方法通过确定击中“阶梯”的级别来分类每个项目。击中的“阶梯”决定了它的类别。
例如,如果您正在编写评分程序,“B”的input范围可能会从75%到90%。 以下是您可能需要编程的一系列等级:
要使用阶梯方法,您需要将每个范围的上限放在一个表格中,然后编写一个循环来检查每个范围上限的分数。 当你发现得分第一次超过一个范围的顶部时,你知道什么是成绩。 使用阶梯技术,您必须小心处理范围的端点。 以下是Visual Basic中的代码,根据此示例为一组学生分配成绩:
虽然这是一个简单的例子,但是您可以轻松地将其推广到处理多个学生,多个评分scheme(例如,不同分配的不同分数级别的不同成绩)以及评分scheme的变化。
Code Complete第2版第426-428页(第18章)。 希望这会有所帮助,对不起,我没有用c ++来转换代码,但是你知道中国古老的一句话:“不要把鱼给乞丐,给他钓鱼竿!” 🙂
如果您以前从未使用过面向对象编程(OOP)语言,那么您会发现OOP使您的生活变得比以往更容易。
你的代码的OOP答案在这里:
(我希望你会在明年编写面向对象的代码)
#include <iostream> #include <exception> #include <stdexcept> using namespace std; class checker { public: checker(void) : m_num(0) { // Nothing. } ~checker(void) { // Nothing. } inline void set_num(int num) { if (num < 0) { throw invalid_argument("Invalid argument: num."); } m_num = num; return; } inline int get_num(void) const { return m_num; } protected: int m_num; }; /**********************************************************/ class multiple_checker : public checker { public: static const int MULTIPLE_OF = 10; multiple_checker(void) { // Nothing. } ~multiple_checker(void) { // Nothing. } virtual void do_this_for_multiple_checker(void) = 0; void check_multiple_of(void) { #if defined _DEBUG if (MULTIPLE_OF == 0) { throw exception("MULTIPLE_OF should not be zero."); } #endif if (m_num % MULTIPLE_OF == 0) { do_this_for_multiple_checker(); } return; } }; /**********************************************************/ class range_checker : public checker { public: range_checker(void) { // Nothing. } ~range_checker(void) { // Nothing. } virtual void do_this_1_for_range_checker(void) = 0; virtual void do_this_2_for_range_checker(void) = 0; void check_in_range(void) { return; } void check_range(void) { if (is_in_range()) { do_this_1_for_range_checker(); } else { do_this_2_for_range_checker(); } } private: bool is_in_range(void) const { if ( (m_num > 10 && m_num < 21) || (m_num > 30 && m_num < 41) || (m_num > 50 && m_num < 61) || (m_num > 70 && m_num < 81) || (m_num > 90 && m_num < 101) ) { return true; } else { return false; } } }; /**********************************************************/ class game : public multiple_checker, public range_checker { public: game(void) { // Nothing. } ~game(void) { // Nothing. } virtual void do_this_for_multiple_checker(void) { cout << "Number is a multiple of " << MULTIPLE_OF << "." << endl; } virtual void do_this_1_for_range_checker(void) { cout << "Number is in range." << endl; } virtual void do_this_2_for_range_checker(void) { cout << "Number is not in range." << endl; } }; /**********************************************************/ int main(int argc, char* argv[]) { game* g = new game(); g->multiple_checker::set_num(50); g->range_checker::set_num(13); g->check_multiple_of(); g->check_range(); delete g; g = NULL; return 0; }