第三次程序-四则运算(结对开发)
接着第二周的程序,这次程序要求结对开发,并要求在第二次的基础上添加用户输入结果判断对错的功能。
结对开发伙伴:
博客名:斗破2
姓名:王文奇
博客链接:http://www.cnblogs.com/qwer111/
功能要求:
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 }
结果展示:
解读:对于程序的提示,采取默认方式显示,分为几个等级,不同等级使用不同功能,当然用户也可以控制一些功能
总结:
这一次是在上一次的基础上的结对开发,这一次的结对开发的程序是我们两相互借鉴上一次程序两人中的优点合并产生的,在修复了两人在程序上的缺点,加快的程序代码实现的速度。
项目计划总结:
时间记入日志:
缺陷记入日志: