四则运算(C语言实现)

时间:2020-04-14
本文章向大家介绍四则运算(C语言实现),主要包括四则运算(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] == '\0')
        {
            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("\n运算结果为:%.2f\n", num[1]);
        fprintf(fp,"%.2f\n",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\n",a,b,c,d,re1,re2);
     fprintf(fp, "%d/%d + %d/%d=%d/%d\n",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\n",a,b,c,d,re1,re2);
        fprintf(fp, "%d/%d - %d/%d=%d/%d\n",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\n",a,b,c,d,re1,re2);
        fprintf(fp, "%d/%d * %d/%d=%d/%d\n",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\n",a,b,c,d,re1,re2);
        fprintf(fp, "%d/%d / %d/%d=%d/%d\n",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= \n",j+1,x,a[i],y);
            fprintf(fp, "%d%c%d= \n",x,a[i],y);
            
            break;
            case 1:
                printf("第%d题:%d%c%d%c%d=  \n",j+1,x,a[i],y,a[b],z);
                fprintf(fp, "%d%c%d%c%d=  \n",x,a[i],y,a[b],z);
                break;
            case 2:
                fprintf(fp, "%d%c%d%c%d%c%d= \n",x,a[i],y,a[b],x1,a[c],z);
                printf("第%d题:%d%c%d%c%d%c%d= \n",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\n");
     exit (1) ;
     }    
     char buf[1024];
     for (i = 0; i < 1024; i++) 
        {    while(fgets(bds,50,fx) != NULL)//输出文件
 {
         
             line = strlen(bds);
                 bds[line-1] = '\0';  /*去掉换行符*/
                     printf("%s  \n",bds);
                    qiuzhi(bds);
                     
         } 
         fclose (fx);
     } 
     } 

  4.主函数

int main()

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

 四、测试

    1.分数计算

    

     2.文件生成

   

    

五、psp表格

    

  六、总结

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

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

原文地址:https://www.cnblogs.com/wenqinyi/p/12700145.html