zoukankan      html  css  js  c++  java
  • 四则运算(C语言实现)

    四则运算(c语言实现)

     合伙人:魏甫——3118004973  ,温钦益——3118004975

     https://github.com/iamdate/work/tree/master 

    一.项目及其要求

      1.题目:实现一个自动生成小学四则运算题目的命令行程序(也可以用图像界面,具有相似功能)。

      2说明:

        自然数:0, 1, 2, …。

      •   真分数:1/2, 1/3, 2/3, 1/4, 1’1/2, …。
      •   运算符:+, −, ×, ÷。
      •   括号:(, )。
      •   等号:=。
      •   分隔符:空格(用于四则运算符和等号前后)。
      •   算术表达式:

          e = n | e1 + e2 | e1 − e2 | e1 × e2 | e1 ÷ e2 | (e),

          其中e, e1和e2为表达式,n为自然数或真分数。

      •   四则运算题目:e = ,其中e为算术表达式。

      3需求:

    1.   使用 -n 参数控制生成题目的个数,例如Myapp.exe -n 10将生成10个题目。
    1.   使用 -r 参数控制题目中数值(自然数、真分数和真分数分母)的范围,例如

    Myapp.exe -r 10

    将生成10以内(不包括10)的四则运算题目。该参数可以设置为1或其他自然数。该参数必须给定,否则程序报错并给出帮助信息。

    1. 生成的题目中计算过程不能产生负数,也就是说算术表达式中如果存在形如e1− e2的子表达式,那么e1≥ e2。
    2. 生成的题目中如果存在形如e1÷ e2的子表达式,那么其结果应是真分数。
    3. 每道题目中出现的运算符个数不超过3个。
    4. 程序一次运行生成的题目不能重复,即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目。例如,23 + 45 = 和45 + 23 = 是重复的题目,6 × 8 = 和8 × 6 = 也是重复的题目。3+(2+1)和1+2+3这两个题目是重复的,由于+是左结合的,1+2+3等价于(1+2)+3,也就是3+(1+2),也就是3+(2+1)。但是1+2+3和3+2+1是不重复的两道题,因为1+2+3等价于(1+2)+3,而3+2+1等价于(3+2)+1,它们之间不能通过有限次交换变成同一个题目。

    生成的题目存入执行程序的当前目录下的Exercises.txt文件,格式如下:

    1. 四则运算题目1
    2. 四则运算题目2

    ……

    其中真分数在输入输出时采用如下格式,真分数五分之三表示为3/5,真分数二又八分之三表示为2’3/8。

    1. 在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件,格式如下:
    1. 答案1
    2. 答案2

    特别的,真分数的运算如下例所示:1/6 + 1/8 = 7/24。

    1. 程序应能支持一万道题目的生成。
    2. 程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计,输入参数如下:

    Myapp.exe -e <exercisefile>.txt -a <answerfile>.txt

    统计结果输出到文件Grade.txt,格式如下:

    Correct: 5 (1, 3, 5, 7, 9)

    Wrong: 5 (2, 4, 6, 8, 10)

    其中“:”后面的数字5表示对/错的题目的数量,括号内的是对/错题目的编号。为简单起见,假设输入的题目都是按照顺序编号的符合规范的题目。

    二、所遇困难及解决办法

      1.如何实现随机运算符和数字?

       解:1.1:每道题目中出现的运算符个数不超过3个相当于e=a*b/c+d,其中未知数有4个,运算符最大值为三个。问:如何产出最多含4个数字的题目

          1.2:经百度得可使用random()函数随机生成数字及运算符,后使用switch语句判断生成题目类型。待续~

       2.如何进行计算?

      解:2.1:题目中所有参数为随机,只有生成后的题目为已知。问:如何读取已知的题目,并进行计算测试?

        2.2:第一次测试:采用数组储存题目,利用switch语句判断运算符。结果:失败,运算符多,计算式复杂,数组可能太少。

        2.3:第二次测试:经询问,采用链表方式,编写逆波兰式将中缀表达式转为后缀表达式进行计算。结果:成功,一般情况下中缀表达式计算比较复杂,但将其转为后缀表达式简洁很多,创建两个栈,一个存放操作数,一个存放运算符,计算时将其拿出。

      3.如何将程序写进文件

      解:3.1:由于之前用java写个人项目的缘故,得知了io数据流,但c和java用法不一,故前去学习。后用fprintf()将生成表达式一并写入文件,再将答案数组也写入文件1。

    三、关键代码

      1.中缀转后缀并计算

    复制代码
    void qiuzhi(char *bds)//中转后并求值 
    {  FILE *fp;
        int i = 0;
        stack *ysf = (stack*)malloc(sizeof(stack));//为表达式开辟一个stack
        ysf->size = 0;
        float num[50];//用于求值的数组
        int numpos = 0;//用于求值的数组位置,因使用较少为提高效率选择数组
        printf("后缀表达式为:");//附加
        while (bds[i] != '=')
        {
            if (bds[i] == '')
            {
                printf("表达式应该有=");
                return;
            }
            if (bds[i] <= '9'&&bds[i] >= '0')//转化数字
            {
                num[++numpos] = 0;
                while (bds[i] <= '9'&&bds[i] >= '0')
                {
                    num[numpos] *= 10;
                    num[numpos] += (bds[i] - '0');
                    ++i;
                }
                if (bds[i] == '.')
                {
                    double f_car = 0.1;//定义基数
                    ++i;
                    while (bds[i] <= '9'&&bds[i] >= '0')
                    {
                        num[numpos] += ((bds[i] - '0')*f_car);
                        f_car *= 0.1;
                        ++i;
                    }
                }//计算小数点
            }
            else
            {
                if (empty(ysf))
                    push(ysf, bds[i]);
                else
                {
                    if (bds[i] == '(')
                        push(ysf, bds[i]);
                    else if (bds[i] == ')')
                    {
                        while (top(ysf) != '(')
                        {
                            reckon(&num[numpos - 1], num[numpos], top(ysf));
                            printf("%c", pop(ysf));
                            --numpos;
                        }
                        pop(ysf);//弹出右括号
                    }
                    else
                    {
                        while (compare(bds[i])<=compare(top(ysf)))
                        {
                            reckon(&num[numpos - 1], num[numpos], top(ysf));
                            printf("%c", pop(ysf));
                            --numpos;
                        }
                        push(ysf, bds[i]);
                    }
                }
                ++i;
            }
        }
        while (!empty(ysf))
        {
            reckon(&num[numpos - 1], num[numpos], top(ysf));
            printf("%c", pop(ysf));
            --numpos;
        }

        fopen("/练习程序/answer.txt","w+");
          printf(" 运算结果为:%.2f ", num[1]);
            fprintf(fp,"%.2f ",num[1]);

    }

    复制代码
    复制代码
    void reckon(float *a, float b, char c)//用于将两数字合并,前数传地址 
    {    //表达式运算定义
        int t;
        if (c == '-')
        {    if(*a<b)//非负 
            {     t=*a;*a=b;b=t;}
            (*a)-=b;
        }
        else if (c == '+')
        {
            (*a) += b;
        }
        else if (c == '*')
        {
            (*a) *= b;
        }
        else     
            if(b!=0)
                (*a) /= b;
                   
    }
    复制代码

     2.分数运算

    复制代码
    void qiuzhi1(int a,int b,int c,int d,char s)
       {  int x,y,t,m;
           float p,q;//中间数 
           int re1,re2,u;//分子分母 
           
        x=getGcd(a,b);//对a,b约分
        a/=x;
        b/=x;
        y=getGcd(c,d);//对c,d约分
        c/=y;
        d/=y;
       FILE *fp=fopen("/练习程序/subject.txt","w+");//读写文件位置
       
       switch(s)//选取运算符
       {
       case '+':
         re1=a*d+c*b;
         re2=b*d;
         t=getGcd(re1,re2);
         re1/=t;
         re2/=t;
         printf("%d/%d + %d/%d=%d/%d
    ",a,b,c,d,re1,re2);
         fprintf(fp, "%d/%d + %d/%d=%d/%d
    ",a,b,c,d,re1,re2);
         break;
       case '-':
           p=a/b;
           q=c/d;
           if(p<q)//判断结果不为负
           {
               u=a;
               a=c;
               c=u;
               u=b;
               b=d;
               d=u;
           }
           re1=a*d-c*b;//结果分子的运算
            re2=b*d;
            t=getGcd(re1,re2);//约分
         re1/=t;
         re2/=t;
            printf("%d/%d - %d/%d=%d/%d
    ",a,b,c,d,re1,re2);
            fprintf(fp, "%d/%d - %d/%d=%d/%d
    ",a,b,c,d,re1,re2);
    
         break;
       case '*':
           re1=a*c;
           re2=b*d;
            t=getGcd(re1,re2);
         re1/=t;
         re2/=t;
         if(a==0||c==0)//有分数为0时
            {
            printf("%d/%d * %d/%d=0",a,b,c,d);
             fprintf(fp, "%d/%d * %d/%d=0",a,b,c,d);
         }
         else
            printf("%d/%d * %d/%d=%d/%d
    ",a,b,c,d,re1,re2);
            fprintf(fp, "%d/%d * %d/%d=%d/%d
    ",a,b,c,d,re1,re2);
            
         break;
       case '/':
           
           re1=a*d;
           re2=b*c;
            t=getGcd(re1,re2);
         re1/=t;
         re2/=t;
         if(a==0)
            {printf("%d/%d / %d/%d=0",a,b,c,d);//结果为0
            fprintf(fp, "%d/%d / %d/%d=0",a,b,c,d);
        }
         else{
            printf("%d/%d / %d/%d=%d/%d
    ",a,b,c,d,re1,re2);
            fprintf(fp, "%d/%d / %d/%d=%d/%d
    ",a,b,c,d,re1,re2);
              }
            
        }
    
    }
    复制代码

      3.读取题目信息

    复制代码
    void creat1(int num,int r)
    {
        char a[]={'+','-','*','/'};
        int X,c,t;
        int i,j,b,count;
        int x,y,x1,y1,z,e;
        int n=sizeof(a);
        srand(time(NULL));
        
        FILE *fp = NULL;//打开文件 
         fp = fopen("/练习程序/test.txt", "w+");
         
        for(j=0;j<num;j++)
        { x=rand()%r;
          y=rand()%r;
          x1=rand()%r;
          y1=rand()%r;
          z=rand()%r;
          X=rand()%4;
          i=rand()%n;
          b=rand()%n;
          c=rand()%n;
          x!=y;
          switch(X)//控制符号数 
            {    case 0:
                 printf("第%d题:%d%c%d= 
    ",j+1,x,a[i],y);
                fprintf(fp, "%d%c%d= 
    ",x,a[i],y);
                
                break;
                case 1:
                    printf("第%d题:%d%c%d%c%d=  
    ",j+1,x,a[i],y,a[b],z);
                    fprintf(fp, "%d%c%d%c%d=  
    ",x,a[i],y,a[b],z);
                    break;
                case 2:
                    fprintf(fp, "%d%c%d%c%d%c%d= 
    ",x,a[i],y,a[b],x1,a[c],z);
                    printf("第%d题:%d%c%d%c%d%c%d= 
    ",j+1,x,a[i],y,a[b],x1,a[c],z);
                    break;
                
            }
            
        }
        fclose(fp);
        
    }
    复制代码
    复制代码
    void save()
    {    stack *p;
        char bds[50];
        FILE *fx;//读取文件 
        int line,i;
        if((fx = fopen("/练习程序/test.txt","r")) == NULL)
         {
         printf("error
    ");
         exit (1) ;
         }    
         char buf[1024];
         for (i = 0; i < 1024; i++) 
            {    while(fgets(bds,50,fx) != NULL)//输出文件
     {
             
                 line = strlen(bds);
                     bds[line-1] = '';  /*去掉换行符*/
                         printf("%s  
    ",bds);
                        qiuzhi(bds);
                         
             } 
             fclose (fx);
         } 
         } 
    复制代码

      4.主函数

    复制代码
    int main()
    
    {        
            int num1,num2,m,n,r,type;
            printf("请选择输出类型:1or2");
            scanf("%d
    ",&type);
            if(type==1)
            {    printf("请输入生成题目数量: ");
                scanf("%d 
    ",&num1);
                printf("请输入分子分母取值范围: ");
                scanf("%d %d",&m,&n);
                creat2(num,m,n);
             } 
             else if(type==2)
             {
              
                printf("请输入生成题目数量: ");
                scanf("%d 
    ",&num2);
                printf("请输入取值范围: ");
                scanf("%d %d",&r);
                creat1(num,r);
                save();//输出答案 
            }
            return 0;
    }
    复制代码

     四、测试

        1.分数计算

        

         2.文件生成

       

        

    五、psp表格

        

      六、总结

      魏甫:这次的项目不能说是很完美的运行,由于基础较差,在经过很多天的学习,实验下,我们仅仅只是完成了个大概,我在我们队伍里是负责测试,审核代码。为了让代码更加美观好看,我将代码整体结构改正了一下,导致我们总体的程序出现了bug,例如整数有时会运行不出,但调试却没有问题。这是我的责任,同时对我来说这个项目并没有结束,之后我会仔细检查debug,让它更完美些。最后一次总结会议上我们在总结自己收获的同时,也互相反思自己在项目上出现了什么问题,有什么不足之处。我们的问题主要有:沟通不到位,由于第一次进行结对项目,难免会有些个人思想,在反思过程中,明显发现,函数中参数设置和定义有很大区别,这让我们不得不去仔细对照查看代码含义。也告诉我在下次结对时,一定一定要在一开始就定好函数和参数的各类功能和类型,提高效率,节省时间。这次的结对对我们来说是一个很好的经历,弥足珍贵。

      温钦益:这次的结对项目,一开始看到题目感觉并不是很难,等到和同伴开始对题目的各个要求完成代码时,才感受到了题目中复杂的地方。我们首先先确定了使用的语言,由于没有对其他语言的学习,故选择了C语言。在完成要求的过程中,我们首先开了个会,先各自完成自己认为能完成的部分。然后把对方的代码发出来一起审核,进行测试。在判断运算符优先级时,意识到要运用数据结构的内容时,又抽出了时间去复习。除此,我们是第一次做结对项目,,在讨论和交流时出现来了偏差。原因是函数定义及参数设置不一,想法未沟通好。在其他的一些功能要求上,由于我们的能力有限,只能完成一部分内容。另外,在交流代码的过程中,学习到了代码的规范的重要性,写的时候要想到如何写才能让对方也能理解你的意思。最后,此次结对项目让我们有了交流的对象,不再是自己一个人的苦思冥想,我们能分享自己遇到的困难,分享自己的经验,让我收获到了许多。

  • 相关阅读:
    CodeForces 408E Curious Array(组合数学+差分)
    CodeForces 519E A and B and Lecture Rooms(倍增)
    洛谷 4051 [JSOI2007]字符加密(后缀数组)
    哇,两门学考都是A(〃'▽'〃)
    BZOJ 1977 严格次小生成树
    XJOI 3606 最大子矩形面积/LightOJ 1083 Histogram(单调栈/笛卡尔树)
    XJOI 3629 非严格次小生成树(pqq的礼物)
    XJOI 3363 树4/ Codeforces 739B Alyona and a tree(树上差分+路径倍增)
    [转载]别让用户发呆—设计中的防呆策略
    Linux下的链接文件
  • 原文地址:https://www.cnblogs.com/wenqinyi/p/12700145.html
Copyright © 2011-2022 走看看