第一周作业——小学四则运算题

作业要求:

  写一个能自动生成小学四则运算题目的命令行 “软件”, 分别满足下面的各种需求,这些需求都可以用命令行参数的形式来指定:

    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),再讲分数转化为字符串,随机生成运算符与随机决定是否括号,再去之前生成的算式拼接。

  对于算式的结果计算,运用典型的中缀表达式转后缀的算法,定义一个符号栈一个分数栈进行计算。

  每次生成一个算式并要求使用者给出自己的答案,判断正误,记录结果。

已经实现功能:

  1. 随机生成整数或者分数算式,整数算式结果为整数,分数算式结果为真分数
  2. 真分数的四则运算,结果为真分数
  3. 支持括号
  4. 支持判断正误,给出准确率

代码:

  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=='
'){
 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] = '';
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]='';
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]='';
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: 
1.integer
2.decimals
3.fraction
";
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 }
View Code

使用说明:

  每次选择算式类型:1.整数 2.小数(还未实现完整)3.分数。再输入题目数量。

  运算数与运算符之间用空格隔开,如:“1/2 / 2“”表示分数1/2除以2,而“1 / 2 / 2”表示1除以2再除以2)

运行截图:

  

  

后期优化计划:

  1. 已经完成小数的栈与表达式的计算部分的代码,后续抽空完成表达式生成的部分。(不过小学生还不会小数吧…)
  2. 运用模板来简化代码(三个栈,两个计算),减少代码量
  3. 支持混合运算。
  4. 支持用户输入运算的范围与算符数量。

暂时无法解决的问题(博主实在弱鸡...请求助教帮助..):

  1. 程序大多时候能正常运行,但是有时候会出错(针对于同样的输入信息有时候会出粗有时候不会,如下面选择整数,产生2道题目)。产生的题目数不够但自动结束,且没有报错信息。。

   

    (错误的)

   

    (正确的)

  2.单步调试的时候,每次在计算函数calculate1(),最后return时,会报下面错误:

    

原文地址:https://www.cnblogs.com/Oranswj/p/5910276.html