作业要求:
写一个能自动生成小学四则运算题目的命令行 “软件”, 分别满足下面的各种需求,这些需求都可以用命令行参数的形式来指定:
a) 支持整数、真分数的四则运算。例如: 1/6 + 1/8 = 7/24
b) 让程序能接受用户输入答案,并判定对错,最后给出总共 对/错 的数量。
c) 逐步扩展功能和可以支持的表达式类型,最后希望能支持下面类型的题目(最多 10 个运算符,括号的数量不限制)
25 - 3 * 4 - 2 / 2 + 89 = ?
1/2 + 1/3 - 1/4 = ?
( 5 - 4 ) * ( 3 +28 ) =?
设计思路:
定义分数类,重载四则运算符+-*/、赋值符号=、逻辑运算符==和输出输出流。
对于整数,看做真分数的一种特例即可。
对于算式的生成,先随机生成运算符的数量(题目要求10个,但是针对于小学生而言..十个还是有点多),采用rand() 随机生成分子分母并化简(整数分子为1),再讲分数转化为字符串,随机生成运算符与随机决定是否括号,再去之前生成的算式拼接。
对于算式的结果计算,运用典型的中缀表达式转后缀的算法,定义一个符号栈一个分数栈进行计算。
每次生成一个算式并要求使用者给出自己的答案,判断正误,记录结果。
已经实现功能:
- 随机生成整数或者分数算式,整数算式结果为整数,分数算式结果为真分数
- 真分数的四则运算,结果为真分数
- 支持括号
- 支持判断正误,给出准确率
代码:
GitHub: https://github.com/chynphh/Elementary-arithmetic
1 #include <iostream> 2 #include <string> 3 #include <string.h> 4 #include <math.h> 5 #include <cstdlib> 6 #include <cstdio> 7 #include <ctime> 8 using namespace std; 9 10 #define OK 1 11 #define ERROR 0 12 #define TRUE 1 13 #define FALSE 0 14 #define MAX 1000 15 #define ADD 100 16 typedef int Status; 17 18 19 // 整数转字符串 20 string itoa_i(int x){ 21 char str[20]; 22 itoa(x, str, 10); 23 return str; 24 25 } 26 27 //求最大公约数 28 int gcd(int m,int n){ 29 if(m%n==0) 30 return n; 31 else 32 return gcd(n,m%n); 33 } 34 35 //分数类 36 class Fraction{ 37 public: 38 Fraction(int x1=0,int z2=1);////构造函数 39 void huajian();//分数化简 40 Fraction operator + (Fraction f2);//运算符重载 + 41 Fraction operator - (Fraction f2);//运算符重载 - 42 Fraction operator * (Fraction f2);//运算符重载 * 43 Fraction operator / (Fraction f2);//运算符重载 / 44 Fraction& operator= (const Fraction& frac){ 45 x = frac.x; 46 z = frac.z; 47 return *this; 48 } 49 bool operator==(const Fraction &c); 50 51 friend void operator >>(istream& in,Fraction &f);//输入流重载 52 friend void operator <<(ostream& out,Fraction &f);//输出流重载 53 54 55 int x;//分子 56 int z;//分母 57 }; 58 59 //输出流重载 60 void operator<<(ostream& out,Fraction &f){ 61 if(f.x==0) out<<" 0"; 62 else if(f.z==1) out<<f.x; 63 else if(f.z==-1) out<<-1*f.z; 64 else{ 65 if(f.x*f.z>0) 66 out<<abs(f.x)<<'/'<<abs(f.z); 67 else 68 out<<-1*abs(f.x)<<'/'<<abs(f.z); 69 70 } 71 } 72 73 //输入流重载 74 void operator>>(istream& in,Fraction &f){ 75 char c; 76 in>>f.x; 77 in.unsetf(ios::skipws);// 78 in>>c; 79 in.setf(ios::skipws);// 80 if(c != '/'){ 81 if(c=='\n'){ 82 f.z = 1; 83 } 84 else{ 85 in>>f.z; 86 throw c; 87 } 88 } 89 else{ 90 in>>f.z; 91 if(f.z == 0) 92 throw f.z; 93 } 94 } 95 96 //逻辑运算符==重载 97 bool Fraction::operator==(const Fraction &c){ 98 if (x == c.x && z == c.z) return true; 99 return false; 100 } 101 102 //分数转字符串 103 string itoa_f(Fraction a){ 104 string s = ""; 105 if(a.z == 1) s = s + itoa_i(a.x) + ""; 106 else s = s + itoa_i(a.x) + "/" + itoa_i(a.z) ; 107 return s; 108 } 109 110 //字符串转整数 111 Fraction atof_f(char* a){ 112 Fraction f; 113 int i = 0, l = 0; 114 char* b; 115 l = strlen(a); 116 while(a[i] != '/' && i < l)i++; 117 if(i < l) { 118 b = a + i + 1; 119 f.z = atoi(b); 120 } 121 else { 122 f.z = 1; 123 } 124 125 a[i] = '\0'; 126 f.x = atoi(a); 127 128 return f; 129 } 130 131 //构造函数 132 Fraction::Fraction(int x1,int z2){ 133 x = x1; 134 z = z2; 135 } 136 137 //分数化简 138 void Fraction::huajian(){ 139 int gc; 140 gc=gcd(abs(x),abs(z)); 141 x/=gc; 142 z/=gc; 143 } 144 145 //运算符重载 + 146 Fraction Fraction::operator +(Fraction f2){ 147 int a, b; 148 Fraction ff; 149 a=x*f2.z+z*f2.x; 150 b=z*f2.z; 151 ff.x=a; 152 ff.z=b; 153 ff.huajian(); 154 return ff; 155 } 156 157 //运算符重载 - 158 Fraction Fraction::operator -(Fraction f2){ 159 int a, b; 160 Fraction ff; 161 a=x*f2.z-z*f2.x; 162 b=z*f2.z; 163 ff.x=a; 164 ff.z=b; 165 ff.huajian(); 166 return ff; 167 } 168 169 //运算符重载 * 170 Fraction Fraction::operator *(Fraction f2){ 171 int a, b; 172 Fraction ff; 173 a=x*f2.x; 174 b=z*f2.z; 175 ff.x=a; 176 ff.z=b; 177 ff.huajian(); 178 return ff; 179 } 180 181 //运算符重载 / 182 Fraction Fraction::operator /(Fraction f2){ 183 if(f2.x==0){ 184 throw f2.x ; 185 } 186 else{ 187 int a, b; 188 Fraction ff; 189 a=x*f2.z; 190 b=z*f2.x; 191 ff.x=a; 192 ff.z=b; 193 ff.huajian(); 194 return ff; 195 } 196 } 197 198 199 200 //定义字符栈 201 typedef struct{ 202 char *top; //指向栈顶 203 char *base; //指向栈底(动态分配内存的首地址) 204 int stacksize; //可用存储空间 205 }stack_char; 206 207 //构造空栈 208 Status InitStack(stack_char &s){ 209 s.base=(char *)malloc(MAX * sizeof(char)); 210 if(!s.base)exit(OVERFLOW);//储存分配失败 211 s.top=s.base; 212 s.stacksize=MAX; 213 return OK; 214 } 215 216 //获取栈顶元素 217 char GetTop(stack_char s,char &e){ 218 if (s.top==s.base)return false; 219 else 220 { 221 e = *(s.top-1); 222 return OK; 223 } 224 } 225 226 //入栈 227 Status Push(stack_char &s, char e) { 228 if(s.top-s.base>=MAX){ 229 s.base = (char *)realloc(s.base,(s.stacksize + ADD)*sizeof(char)); 230 if(!s.base)exit(OVERFLOW); 231 s.top=s.base+s.stacksize; 232 s.stacksize =s.stacksize + ADD; 233 } 234 *s.top=e; 235 s.top++; 236 return OK; 237 } 238 239 //出栈 240 Status Pop(stack_char &s,char &e){ 241 if(s.top==s.base)return ERROR; 242 s.top--; 243 e=*s.top; 244 return OK; 245 } 246 247 248 //定义小数栈 249 typedef struct{ 250 double *top; //指向栈顶 251 double *base; //指向栈底(动态分配内存的首地址) 252 int stacksize; //可用存储空间 253 }stack_double; 254 255 //构造空栈 256 Status InitStack(stack_double &s){ 257 s.base=(double *)malloc(MAX * sizeof(double)); 258 if(!s.base)exit(OVERFLOW);//储存分配失败 259 s.top=s.base; 260 s.stacksize=MAX; 261 return OK; 262 } 263 264 //获取栈顶元素 265 Status GetTop(stack_double s,double &e){ 266 if (s.top==s.base)return false; 267 else 268 { 269 e = *(s.top-1); 270 return OK; 271 } 272 } 273 274 //入栈 275 Status Push(stack_double &s, double e) { 276 if(s.top-s.base>=MAX){ 277 s.base = (double *)realloc(s.base,(s.stacksize + ADD)*sizeof(double)); 278 if(!s.base)exit(OVERFLOW); 279 s.top=s.base+s.stacksize; 280 s.stacksize =s.stacksize + ADD; 281 } 282 *s.top=e; 283 s.top++; 284 return OK; 285 } 286 287 //出栈 288 Status Pop(stack_double &s,double &e){ 289 if(s.top==s.base)return ERROR; 290 s.top--; 291 e=*s.top; 292 return OK; 293 } 294 295 //定义分数栈 296 typedef struct{ 297 Fraction *top; //指向栈顶 298 Fraction *base; //指向栈底(动态分配内存的首地址) 299 int stacksize; //可用存储空间 300 }stack_Fraction; 301 302 //构造空栈 303 Status InitStack(stack_Fraction &s){ 304 s.base=(Fraction *)malloc(MAX * sizeof(Fraction)); 305 if(!s.base)exit(OVERFLOW);//储存分配失败 306 s.top=s.base; 307 s.stacksize=MAX; 308 return OK; 309 } 310 311 //获取栈顶元素 312 Status GetTop(stack_Fraction s,Fraction &e){ 313 if (s.top==s.base)return false; 314 else 315 { 316 e = *(s.top-1); 317 return OK; 318 } 319 } 320 321 //入栈 322 Status Push(stack_Fraction &s, Fraction e) { 323 if(s.top-s.base>=MAX){ 324 s.base = (Fraction *)realloc(s.base,(s.stacksize + ADD)*sizeof(Fraction)); 325 if(!s.base)exit(OVERFLOW); 326 s.top=s.base+s.stacksize; 327 s.stacksize =s.stacksize + ADD; 328 } 329 *s.top = e; 330 s.top++; 331 return OK; 332 } 333 334 //出栈 335 Status Pop(stack_Fraction &s,Fraction &e){ 336 if(s.top==s.base)return ERROR; 337 s.top--; 338 e = *s.top; 339 return OK; 340 } 341 342 char op[7]={ '+','-','*','/','(',')', '#'}; 343 //优先权集合 344 char priority[7][7]= 345 { {'>','>','<','<','<','>','>'}, 346 {'>','>','<','<','<','>','>'}, 347 {'>','>','>','>','<','>','>'}, 348 {'>','>','>','>','<','>','>'}, 349 {'<','<','<','<','<','=','@'}, 350 {'>','>','>','>','@','>','>'}, 351 {'<','<','<','<','<','@','='} }; 352 353 char Precede(char a,char b){ 354 int i,j; 355 for(int k=0;k<7;k++){ 356 if(op[k]==a)i=k; 357 if(op[k]==b)j=k; 358 } 359 return priority[i][j]; 360 } 361 362 bool In(char e,char *a){ 363 int l; 364 l=strlen(a); 365 bool temp=false; 366 for(int i=0;i<l;i++) 367 { 368 if(a[i]==e)temp=true; 369 } 370 return temp; 371 } 372 373 template<typename T> 374 T Operate(T x,char c,T y) 375 { 376 switch(c) 377 { 378 case'+':return x + y;break; 379 case'-':return x - y;break; 380 case'*':return x * y;break; 381 case'/':return x / y;break; 382 } 383 } 384 385 //分数计算 386 Fraction calculate1(string str){ 387 388 stack_char optr; 389 stack_Fraction opnd_f; 390 391 InitStack(optr); 392 InitStack(opnd_f); 393 394 int l; 395 l = str.size(); 396 str[l] = ' '; 397 l++; 398 str[l] = '#'; 399 l++; 400 Push(optr,'#'); 401 402 int i=0; 403 char t; 404 GetTop(optr,t); 405 while((str[i]!='#'|| t!='#') && i < l){ 406 if((str[i]=='(' && str[i+1]!='-') || (In(str[i],op)==1 && str[i]!='(')){ 407 char x,theta; 408 Fraction a,b; 409 GetTop(optr,t); 410 switch(Precede(t,str[i])){ 411 case '<': 412 Push(optr,str[i]); 413 i++; 414 break; 415 case '=': 416 Pop(optr,x); 417 i++; 418 break; 419 case '>': 420 Pop(optr,theta); 421 Pop(opnd_f,b); 422 Pop(opnd_f,a); 423 Push(opnd_f,Operate(a,theta,b)) ; 424 break; 425 } 426 } 427 else{ 428 int k=0; 429 char temp[20]; 430 Fraction x; 431 while (str[i+k+1]!=' '){ 432 k++; 433 } 434 strncpy(temp,&str[i],k+1); 435 temp[k+1]='\0'; 436 x = atof_f(temp); 437 Push(opnd_f,x); 438 i=i+k+1; 439 } 440 while(str[i]==' ')i++; 441 GetTop(optr,t); 442 } 443 444 Fraction answer; 445 GetTop(opnd_f,answer); 446 // cout << answer; 447 // cout << endl; 448 return answer; 449 } 450 451 //小数计算 452 double calculate2(string str){ 453 454 stack_char optr; 455 stack_double opnd_d; 456 InitStack(optr); 457 InitStack(opnd_d); 458 459 460 char l; 461 l = str.size(); 462 str[l]='#'; 463 l++; 464 Push(optr,'#'); 465 466 int i=0; 467 char t; 468 GetTop(optr,t); 469 470 while(str[i]!='#'|| t!='#') 471 { 472 if( In(str[i],op)==1 && str[i]!='(' ) 473 { 474 char x,theta; 475 double a,b; 476 GetTop(optr,t); 477 switch(Precede(t,str[i])){ 478 case '<': 479 Push(optr,str[i]); 480 i++; 481 break; 482 case '=': 483 Pop(optr,x); 484 i++; 485 break; 486 case '>': 487 Pop(optr,theta); 488 Pop(opnd_d,b); 489 Pop(opnd_d,a); 490 Push(opnd_d,Operate(a,theta,b)) ; 491 break; 492 } 493 } 494 else{ 495 int k=0; 496 char temp[20]; 497 double x; 498 while((!In(str[i+k+1],op)) || (In(str[i+k+1],op) && str[i+k+2]!=' ')){ 499 k++; 500 } 501 strncpy(temp,&str[i],k+1); 502 temp[k+1]='\0'; 503 x = atof(temp); 504 Push(opnd_d,x); 505 i=i+k+1; 506 while(str[i]==' ')i++; 507 } 508 GetTop(optr,t); 509 } 510 511 double answer; 512 GetTop(opnd_d,answer); 513 514 return answer; 515 } 516 517 char opp(int op){ 518 switch(op){ 519 case 0: return '+'; 520 case 1: return '-'; 521 case 2: return '*'; 522 case 3: return '/'; 523 } 524 } 525 526 //随机生成算式 527 string Create(int type){ 528 int num_op = 0; 529 string exp = ""; 530 num_op = rand()%10 + 1;//符号个数 531 Fraction a; 532 int b, c, d; // b是符号,c是左右部,d是有无括号 533 534 switch(type){ 535 case 1: 536 a.x = rand()%100 + 1; 537 a.z = 1; 538 a.huajian(); 539 exp = exp + itoa_f(a); 540 while(num_op--){ 541 b = rand()%4; 542 d = rand()%3; 543 c = rand()%2; 544 if(b == 3) a.x = rand()%5 + 1; //除法设置除数较小 545 else if (b == 2) a.x = rand()%20 +1; // 乘法设置乘数相对较小 546 else a.x = rand()%100 +1; 547 a.huajian(); 548 if(c){ //当做左部 549 exp = exp + ' ' + opp(b) + ' ' + itoa_f(a); 550 } 551 else{ // 当做右部 552 exp = itoa_f(a) + ' ' + opp(b) + ' ' + exp; 553 } 554 if(b == 0 && num_op > 0){ 555 exp = "( " + exp + " )"; 556 } 557 } 558 break; 559 case 2 : 560 break; 561 case 3: 562 a.x = rand()%30 + 1; 563 a.z = rand()%10 + 1; 564 a.huajian(); 565 exp = exp + itoa_f(a); 566 while(num_op--){ 567 b = rand()%4; 568 d = rand()%3; 569 c = rand()%2; 570 if(c){ //当做左部 571 a.x = rand()%30 + 1; 572 a.z = rand()%10 + 1; 573 a.huajian(); 574 exp = exp + ' ' + opp(b) + ' ' + itoa_f(a); 575 } 576 else{ // 当做右部 577 a.x = rand()%30 + 1; 578 a.z = rand()%10 + 1; 579 exp = itoa_f(a) + ' ' + opp(b) + ' ' + exp; 580 } 581 if(b == 0 && num_op > 0){ 582 exp = "( " + exp + " )"; 583 } 584 } 585 break; 586 } 587 return exp; 588 } 589 590 int start(int num, int type){ 591 int num_right = 0; 592 string exp; 593 Fraction std_ans_f, ans_f; 594 double std_ans_d = 0.0, ans_d = 0.0; 595 596 while(num--){ 597 exp = Create(type); 598 if(type != 2){//整数 或者 真分数运算 599 std_ans_f = calculate1(exp); 600 if(type == 1)//整数运算保证结果为整数 601 while(std_ans_f.z != 1){ 602 exp = Create(type); 603 std_ans_f = calculate1(exp); 604 } 605 cout << "Q:"<< exp << " " << endl; 606 cout << "your ans:" ; 607 cin >> ans_f; 608 cout << "the right ans:"; 609 cout << std_ans_f ; 610 cout << endl << endl; 611 612 if(std_ans_f == ans_f)num_right++; 613 } 614 else{//小数部分 未完善 615 616 } 617 exp=""; 618 } 619 return num_right; 620 } 621 622 623 int main () 624 { 625 srand(time(0)); //初始化随机数发生器 626 627 int type = -1, num = 0, num_right = 0; 628 double acc = 0.0; 629 cout << "Please select the type of problem: \n1.integer\n2.decimals\n3.fraction\n"; 630 cin >> type; 631 while(type < 1 || type > 3){ 632 cout << "Please select the correct type of problem" << endl; 633 cin >> type; 634 } 635 cout << "please input the number of problems " << endl; 636 cin >> num; 637 num_right = start(num, type); 638 acc = num_right * 1.0 / num; 639 cout << "The number of question : " << num << endl; 640 cout << "The number of correct answer : "<< num_right << endl; 641 cout << "your accuracy is " << acc <<endl; 642 return 0; 643 }
使用说明:
每次选择算式类型:1.整数 2.小数(还未实现完整)3.分数。再输入题目数量。
运算数与运算符之间用空格隔开,如:“1/2 / 2“”表示分数1/2除以2,而“1 / 2 / 2”表示1除以2再除以2)
运行截图:
后期优化计划:
- 已经完成小数的栈与表达式的计算部分的代码,后续抽空完成表达式生成的部分。(不过小学生还不会小数吧…)
- 运用模板来简化代码(三个栈,两个计算),减少代码量
- 支持混合运算。
- 支持用户输入运算的范围与算符数量。
暂时无法解决的问题(博主实在弱鸡...请求助教帮助..):
- 程序大多时候能正常运行,但是有时候会出错(针对于同样的输入信息有时候会出粗有时候不会,如下面选择整数,产生2道题目)。产生的题目数不够但自动结束,且没有报错信息。。
(错误的)
(正确的)
2.单步调试的时候,每次在计算函数calculate1(),最后return时,会报下面错误: