四则运算3(结对开发)
本次试验是由上次的基础上迭代开发而来,并且用结对开发的模式进行开发。
结对开发的伙伴:
博客名:Mr.缪
姓名:缪金敏
链接:http://www.cnblogs.com/miaojinmin799/
功能要求:
1、乘除可控
2、随机添加括号
3、输入结果判断正误
4、统计正确数量
5、正负,余数可控
6、去除连除误区
程序思想:
1、无乘除可用0,1表示,有乘除可再加2,3,共四种选择
2、用随机函数选择左括号放在哪个操作数前面,然后再用随机函数添加右括号,在使用循环判断去除左括号加在最左边右括号加在最右边的式子
3、使用栈的思想,把生成的公式压入栈中,根据优先关系再进行计算,得出的结果存在一个数组中,当用户输入时和数组中的数比较
4、通过使用栈算出结果便可以知道有无正负和余数,可使用循环重新生成式子
5、在生成式子时,记入上一个运算符,当出现第二个“/”号时自动重新生成式子
6、最简真分数表示:求出分子分母最大公约数,然后都除以这个数,从而得出最简真分数,真分数用“(XX/XX)”表示
所遇困难:
1、在遇到10或20等数时,会出现运算错误,经过调试发现在出栈提取第二个操作数时总是为0,后经过查看发现在读取操作数时每遇到0就会多压入一个操作数导致运算混乱
2、对于运算式子的计算优先级的判断不是很了解,后来参考了原先数据结构课本中的资料,在结合程序本身情况完成了对运算式计算的函数。
结对开发过程照片:
源代码:
1 #include<iostream> 2 #include<string> 3 #include<sstream> 4 #include<time.h> 5 #include<iomanip> 6 #include<fstream> 7 #define MAX 100 8 using namespace std; 9 10 stringstream formula; //当前算式 11 string buffer[MAX]; //缓冲区数组 12 int TopNumber; //上限 13 int BaseNumber; //下限 14 int IsMulDlvExist; //是否有乘除 15 int Amount; //操作数的个数 16 int BracketNum; //括号个数 17 int LBraket[2]; //左括号的位置 18 int RBraket[2]; //右括号的位置 19 int IsNeg; //是否有负数 20 int IsRem; //是否有余数 21 int IsBra; //是否有括号 22 int IsRep; //是否重复 23 float Result[MAX]; //正确结果数组 24 char lastOp; //记录上个运算符是否为除号 25 26 //优先级数组 27 char prior[7][7] = { 28 { '>', '>', '<', '<', '<', '>', '>' }, 29 { '>', '>', '<', '<', '<', '>', '>' }, 30 { '>', '>', '>', '>', '<', '>', '>' }, 31 { '>', '>', '>', '>', '<', '>', '>' }, 32 { '<', '<', '<', '<', '<', '=', ' ' }, 33 { '>', '>', '>', '>', ' ', '>', '>' }, 34 { '<', '<', '<', '<', '<', ' ', '=' } 35 }; 36 //操作符栈 37 typedef struct { 38 char *base; 39 char *top; 40 }OperChar; 41 //操作数栈 42 typedef struct{ 43 float *base; 44 float *top; 45 }NumberLink; 46 //初始化栈 47 void InitOperStack(OperChar &S) 48 { 49 S.base = new char[MAX]; 50 if (!S.base) 51 exit(1); 52 S.top = S.base; 53 } 54 void InitNumStack(NumberLink &S) 55 { 56 S.base = new float[MAX]; 57 if (!S.base) 58 exit(1); 59 S.top = S.base; 60 } 61 //进栈 62 void PushOper(OperChar &S, char e){ 63 if (S.top - S.base == MAX) 64 exit(1); 65 *S.top++ = e; 66 67 } 68 void PushNum(NumberLink &S, float e){ 69 if (S.top - S.base == MAX) 70 exit(1); 71 *S.top++ = e; 72 } 73 //出栈 74 void PopOper(OperChar &S, char &e) 75 { 76 if (S.top == S.base) 77 exit(1); 78 e = *--S.top; 79 } 80 void PopNum(NumberLink &S, float &e) 81 { 82 if (S.top == S.base) 83 exit(1); 84 e = *--S.top; 85 } 86 //取栈顶元素 87 char GetTopOper(OperChar S) 88 { 89 if (S.top == S.base) 90 { 91 exit(1); 92 93 } 94 return *(S.top - 1); 95 } 96 float GetTopNum(NumberLink S) 97 { 98 if (S.top == S.base) 99 { 100 exit(1); 101 102 } 103 return *(S.top - 1); 104 } 105 //将操作符转化为优先级数组的下标 106 int Change(char Oper) 107 { 108 switch (Oper) 109 { 110 case '+': return 0; break; 111 case '-': return 1; break; 112 case '*': return 2; break; 113 case '/': return 3; break; 114 case '(': return 4; break; 115 case ')': return 5; break; 116 case '=': return 6; break; 117 default: return 6; break; 118 119 } 120 } 121 //返回优先级的大小 122 char Precede(char Oper, char ch) 123 { 124 return prior[Change(Oper)][Change(ch)]; 125 } 126 //计算两个数的结果 127 float Operate(float first, char oper1, float second) 128 { 129 switch (oper1) 130 { 131 case '+': 132 { 133 return (first + second); 134 break; 135 } 136 case '-': 137 { 138 return (first - second); 139 break; 140 } 141 case '*': 142 { 143 return (first * second); 144 break; 145 } 146 case '/': 147 { 148 if (second == 0) 149 { 150 IsRep = 1; 151 return 0; 152 } 153 return (first / second); 154 break; 155 156 } 157 default: return 0; break; 158 } 159 } 160 //数字的个数 161 void NumberAmount() 162 { 163 Amount = 2 + rand() % 5; 164 } 165 //加左括号 随机选择在第几个数字前面加括号 166 void AddLbracket(){ 167 for (int j = 0; j < 2; j++) 168 LBraket[j] = 0; 169 if (Amount == 2) 170 { 171 BracketNum = 0; 172 } 173 if (Amount == 3){ 174 BracketNum = rand() % 2; 175 } 176 if (Amount > 3) 177 { 178 BracketNum = rand() % 3; 179 } 180 for (int i = 0; i < BracketNum; i++){ 181 LBraket[i] = 1 + rand() % (Amount - 2); 182 } 183 } 184 //加右括号 185 void AddRbracket(){ 186 for (int j = 0; j < 2; j++) 187 RBraket[j] = 0; 188 int choose; 189 int trance; 190 if (BracketNum == 1){ 191 RBraket[0] = LBraket[0] + 1 + rand() % (Amount - LBraket[0]); 192 } 193 if (BracketNum == 2) 194 195 { 196 //把最左边的左括号放在第一个数组中 197 if (LBraket[0] < LBraket[1]) 198 { 199 trance = LBraket[0]; 200 LBraket[0] = LBraket[1]; 201 LBraket[1] = trance; 202 } 203 //当两个左括号之间相差有点远时有2中右括号添加方法 204 if (LBraket[0] - LBraket[1]>2){ 205 choose = rand() % 2; 206 if (choose == 0){ 207 RBraket[0] = LBraket[0] + 1 + rand() % (Amount - LBraket[0]); 208 RBraket[1] = LBraket[0] + 1 + rand() % (Amount - LBraket[0]); 209 } 210 if (choose == 1) 211 { 212 RBraket[0] = LBraket[0] + 1 + rand() % (Amount - LBraket[0]); 213 RBraket[1] = LBraket[1] + 1 + rand() % (LBraket[0] - 2); 214 } 215 } 216 else 217 { 218 RBraket[0] = LBraket[0] + 1 + rand() % (Amount - LBraket[0]); 219 RBraket[1] = LBraket[0] + 1 + rand() % (Amount - LBraket[0]); 220 if (LBraket[0] == LBraket[1] && RBraket[0] == RBraket[1]){ 221 LBraket[0] = LBraket[1] = 0; 222 RBraket[0] = RBraket[1] = 0; 223 BracketNum = 0; 224 225 } 226 if (LBraket[1] == 1 && (RBraket[0] == Amount || RBraket[1] == Amount)) 227 { 228 LBraket[0] = LBraket[1] = 0; 229 RBraket[0] = RBraket[1] = 0; 230 BracketNum = 0; 231 } 232 233 } 234 } 235 } 236 //随机产生最简真分数 237 void Score(){ 238 int Left, Right; 239 Left = BaseNumber + rand() % (TopNumber - BaseNumber + 1); 240 Right = BaseNumber + rand() % (TopNumber - BaseNumber + 1); 241 while (Left >= Right || Left == 0) 242 { 243 Left = BaseNumber + rand() % (TopNumber - BaseNumber + 1); 244 Right = BaseNumber + rand() % (TopNumber - BaseNumber + 1); 245 } 246 int max = 1; 247 //求最大公约数 248 for (int i = 2; i <= Left; i++) 249 { 250 if (Left%i == 0 && Right%i == 0) 251 { 252 max = i; 253 } 254 } 255 if (max > 1) 256 { 257 Left /= max; 258 Right /= max; 259 } 260 formula << '(' << Left << '/' << Right << ')'; 261 } 262 //随机生成操作符 263 void Operater() 264 { 265 int choose; 266 char op; 267 if (IsMulDlvExist == 1) 268 choose = 1 + rand() % 4; 269 else 270 choose = 1 + rand() % 2; 271 272 switch (choose) 273 { 274 case 1:{op = '+'; lastOp = '+'; break; } 275 case 2:{op = '-'; lastOp = '-'; break; } 276 case 3:{op = '*'; lastOp = '*'; break; } 277 case 4: 278 { 279 //防止连续除法产生运算误区 280 op = '/'; 281 if (lastOp == '/') 282 IsRep = 1; 283 else 284 lastOp = '/'; 285 break; 286 } 287 } 288 formula << op; 289 } 290 //随机生成整数 291 void Integer(){ 292 int num; 293 num = BaseNumber + rand() % (TopNumber - BaseNumber + 1); 294 formula << num; 295 } 296 //创建算式 297 void CreateNumber(){ 298 for (int k = 1; k <= Amount; k++) 299 { 300 301 for (int i = 0; i < 2; i++){ 302 if (LBraket[i] == k) 303 formula << '('; 304 } 305 306 int cho; 307 cho = rand() % 2; 308 if (cho == 0) 309 { 310 Integer(); 311 } 312 else 313 Score(); 314 for (int j = 0; j < 2; j++){ 315 if ((RBraket[j] == k) && RBraket[j] != 0) 316 formula << ')'; 317 } 318 if (k == Amount) 319 formula << '='; 320 else 321 Operater(); 322 } 323 } 324 //检查是否重复及判断括号是否添加正确 325 int Repeat(int time){ 326 buffer[time] = formula.str(); 327 int juege = 0; 328 int trance; 329 for (int i = 0; i < time; i++) 330 { 331 if (buffer[i] == buffer[time]) 332 { 333 juege = 1; 334 break; 335 } 336 } 337 if (IsBra != 1) 338 { 339 if (BracketNum == 1) 340 { 341 if (LBraket[0] == 1 && RBraket[0] == Amount) 342 juege = 1; 343 } 344 if (BracketNum == 2) 345 { 346 if (RBraket[0] < RBraket[1]) 347 { 348 trance = RBraket[0]; 349 RBraket[0] = RBraket[1]; 350 RBraket[1] = trance; 351 } 352 if (LBraket[1] == 1 && RBraket[0] == Amount&&LBraket[0] < RBraket[1]) 353 juege = 1; 354 } 355 } 356 return juege; 357 358 } 359 //利用栈计算结果 参考《数据结构---C语言》 360 float EvaluateExpression(){ 361 OperChar OPTR; 362 NumberLink OPND; 363 InitOperStack(OPTR); 364 PushOper(OPTR, '='); 365 InitNumStack(OPND); 366 int count = 0; 367 float Num = 0, first, second; 368 char oper1; 369 char bracket1; 370 while (true) 371 { 372 Num = 0; 373 //读取数字 374 while (formula.str()[count] >= '0'&&formula.str()[count] <= '9') 375 { 376 if (formula.str()[count] == '0') 377 { 378 if (count == 0) 379 PushNum(OPND, 0); 380 if (count != 0 && !(formula.str()[count - 1] >= '0'&&formula.str()[count - 1] <= '9')) 381 PushNum(OPND, 0); 382 } 383 384 Num = Num * 10; 385 Num = Num + formula.str()[count] - 48; 386 count++; 387 388 } 389 if (Num > 0) 390 { 391 PushNum(OPND, Num); 392 } 393 if (formula.str()[count] == '='&&GetTopOper(OPTR) == '=') 394 { 395 break; 396 } 397 //判断运算符优先级 398 switch (Precede(GetTopOper(OPTR), formula.str()[count])) 399 { 400 case '<': 401 { 402 PushOper(OPTR, formula.str()[count]); 403 count++; 404 break; 405 } 406 case '>': 407 { 408 PopOper(OPTR, oper1); 409 PopNum(OPND, second); 410 PopNum(OPND, first); 411 PushNum(OPND, Operate(first, oper1, second)); 412 break; 413 } 414 case '=': 415 { 416 PopOper(OPTR, bracket1); 417 count++; 418 } 419 } 420 } 421 return GetTopNum(OPND); 422 } 423 int main() 424 { 425 ofstream out("1.txt", ios::out); 426 int OutChoose = 0; 427 int truenum = 0; 428 int choose; 429 bool flag = true; 430 int range = 0; 431 srand((unsigned)time(NULL)); 432 /*cin >> IsMulDlvExist; 433 cin >> BaseNumber; 434 cin >> TopNumber; 435 cin >> IsNeg; 436 cin >> IsRem; 437 cin >> IsBra;*/ 438 cout << " 欢迎来到四则运算答题系统!" << endl; 439 cout << "说明:\n\t初级只有加减法无括号无负数无余数(默认数值范围0-5)\n\t中级有乘除有括号无负数无余数(默认范围0-20)\n\t高级有乘除有括号有负数有余数(默认范围0-100)" << endl; 440 while (flag) 441 { 442 cout << "现在有初级,中级,高级,三种关卡,你要挑战哪一关?" << endl; 443 cout << "1.初级 2.中级 3.高级 请选择:"; 444 cin >> choose; 445 switch (choose) 446 { 447 case 1: 448 { 449 cout << "是否打印试卷?0、否 1、是 请选择 : "; 450 cin >> OutChoose; 451 cout << "是否需要数值重设范围?0、否 1、是 请选择 : "; 452 cin >> range; 453 IsMulDlvExist = 0; 454 BaseNumber = 0; 455 TopNumber = 5; 456 if (range == 1){ 457 cout << "请输入下限(正数):"; 458 cin >> BaseNumber; 459 cout << "请输入上限(正数):"; 460 cin >> TopNumber; 461 } 462 IsNeg = 1; 463 IsRem = 1; 464 IsBra = 1; 465 flag = false; 466 break; 467 } 468 case 2: 469 { 470 cout << "是否打印试卷?0、否 1、是 请选择 : "; 471 cin >> OutChoose; 472 cout << "是否需要数值重设范围?0、否 1、是 请选择 : "; 473 cin >> range; 474 IsMulDlvExist = 1; 475 BaseNumber = 0; 476 TopNumber = 20; 477 if (range == 1){ 478 cout << "请输入下限(正数):"; 479 cin >> BaseNumber; 480 cout << "请输入上限(正数):"; 481 cin >> TopNumber; 482 } 483 IsNeg = 1; 484 IsRem = 1; 485 IsBra = 0; 486 flag = false; 487 break; 488 } 489 case 3: 490 { 491 cout << "是否打印试卷?0、否 1、是 请选择 : "; 492 cin >> OutChoose; 493 cout << "是否需要数值重设范围?0、否 1、是 请选择 : "; 494 cin >> range; 495 IsMulDlvExist = 1; 496 BaseNumber = 0; 497 TopNumber = 100; 498 if (range == 1){ 499 cout << "请输入下限(正数):"; 500 cin >> BaseNumber; 501 cout << "请输入上限(正数):"; 502 cin >> TopNumber; 503 } 504 IsNeg = 0; 505 IsRem = 0; 506 IsBra = 0; 507 flag = false; 508 break; 509 } 510 default: 511 { 512 cout << "输入有误,请重新选择:" << endl; 513 flag = true; 514 break; 515 } 516 } 517 518 } 519 520 521 float sum = 0; 522 for (int i = 0; i < 30; i++) 523 { 524 lastOp = '+'; 525 IsRep = 0; 526 NumberAmount(); 527 if (IsBra == 0) 528 { 529 AddLbracket(); 530 AddRbracket(); 531 } 532 CreateNumber(); 533 Result[i] = EvaluateExpression(); 534 while (Repeat(i) == 1 || IsRep == 1 || (IsNeg == 1 && (Result[i] < 0 || Result[i] == -0)) || (IsRem == 1 && (int(Result[i] * 10000)) % 10000 != 0)) 535 { 536 537 IsRep = 0; 538 lastOp = '+'; 539 formula.str(""); 540 NumberAmount(); 541 if (IsBra == 0) 542 { 543 AddLbracket(); 544 AddRbracket(); 545 } 546 CreateNumber(); 547 Result[i] = EvaluateExpression(); 548 } 549 cout << "第" << i + 1 << "题:"; 550 cout << formula.str(); 551 552 cin >> sum; 553 if (OutChoose == 1) 554 { 555 out << "第" << i + 1 << "题:"; 556 out << formula.str() << sum; 557 } 558 if (abs(int(100 * sum) - int(100 * Result[i])) <= 1) 559 { 560 cout << "小朋友太棒了,回答正确!\n"; 561 if (OutChoose == 1) 562 out << "小朋友太棒了,回答正确!\n"; 563 truenum++; 564 } 565 else 566 { 567 cout << "回答错误,小朋友继续加油!正确答案:" << Result[i] << endl; 568 if (OutChoose == 1) 569 out << "回答错误,小朋友继续加油!正确答案:" << Result[i] << endl; 570 } 571 572 formula.str(""); 573 } 574 cout << endl; 575 cout << "你一共答对了" << truenum << "道题!" << endl; 576 if (OutChoose == 1) 577 out << "你一共答对了" << truenum << "道题!" << endl; 578 return 0; 579 }
运行结果截图:
编程总结分析:
一开始我们接着上次的基础做,发觉根本行不通,所以我们几乎是重写了全部的代码,从随机生成数开始一步步的写,觉得加括号和利用栈来计算结果是最难实现的部分。尤其是利用栈来计算结果这个函数及其子函数错了好多次,总是有问题,我们通过debug调试断点,一步步查才逐渐找到bug,并修复了它。总之,本次程序是十分具有挑战性的,多亏了和我的好伙伴一起努力才完成了这次任务。
项目计划总结:
时间记入日志:
缺陷记入日志: