高级软件工程第二次作业--四则运算生成器

1 项目 GitHub 地址

        https://github.com/shuangshuanggit

2 预估耗时与实际耗时

PSP2.1Personal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning 计划 30  60
· Estimate · 估计这个任务需要多少时间 120  240
Development 开发 300   780
· Analysis · 需求分析 (包括学习新技术)   30   40
· Design Spec · 生成设计文档   0   0
· Design Review · 设计复审 (和同事审核设计文档)   0   30
· Coding Standard · 代码规范 (为目前的开发制定合适的规范)   0   0
· Design · 具体设计   30   60
· Coding · 具体编码   120  500
· Code Review · 代码复审   60   30
· Test · 测试(自我测试,修改代码,提交修改)   60   120
Reporting 报告 180   150
· Test Report · 测试报告   150   120
· Size Measurement · 计算工作量   10   10
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划   20  20
合计   510  990

3 解题思路

        为了稍微好上手,还是选择了最熟悉的C语言。

        1.根据要求,随机生成四则运算的长度(操作符个数小于10),n=rand()%10+1,生成操作数个数;

        2.操作数随机生成,保存在整形数组a[n]中,其中a[2i]时保存的1~100的数字,rand()%100+1,所以后面不会再考虑除数是否为0;

        3.把分数也看成除法,所以整个式子就是整数的加减乘除,在这里,把a[2i+1]里面的数看成操作符,rand()%4-4,随机生成的-4、-3、-2、-1分别代表“+”、“-”、“*”、“/”;

        4.能够保存用户的输入,并判断对错,我的想法是把最后结果转化为字符串,用户的输入也用字符串保存(如果输入为整数则只与最后结果‘/’之前的字符串进行比较),进行字符串匹配,判断是否正确;

        5.计算正确率,只需要用一个count来记录即可,最后count*100/n,然后用%.2f表示即可;

        6.其中关键就是如何计算这个四则运算式,在我考虑了乱七八糟的很长时间后,室友提醒我用逆波兰式,然后就百度,看了一位博主的分析,原理分析很细致,然后就顺着这个思路来往下继续写。主要是将结果保存在另外一个整型数组中,得到最终结果后,转化为字符串。

        总结一下,大致为: 随机运算符个数、随机操作数、随机运算符保存在数组中-->然后转化为后缀表达式-->计算后缀表达式-->与输入的结果进行匹配

4设计实现和代码说明

        有4个被调函数,生成表达数create()函数,转换后缀表达式trans()函数、后缀表达数计算calculate()函数、最大公约数gcd()函数(用于分数化简)。

4.1 随机运算符个数、操作符、运算符的生成

         C语言中有生成随机数的函数,因为对每句代码如何写还是不知道,所以每写一句就会百度一下,过程中才知道应该使用srand()函数,然后写在主函数中:

void int main()
{
    int n,i,c;
    printf("请输入需要生成的四则运算式的个数:");
    scanf("%d",&c);
    for(i=0;i<n;i++)
    {
      srand( (unsigned int)time( NULL ) ); //初始化随机数
      n=rand()%10+1;//随机生成表达式长度,有1~10个操作符
      //生成运算表达式等等操作
     } 
        //其他操作
}

4.2生成运算表达式

 随机生成四则运算表达式create()函数:

int create(int n,int a[])
 {
     //首先随机生成一个运算表达式 
     int m,i,j,t,top=0; 
     m=2*(n-1);//表达式有多少个操作数,就要用相应长度的数组来保存     
     
    //随机生成表达式 
    for(i=0;i<=m;i++)
    {
        if(i%2==0)
        {
            a[i]=rand()%100+1;//1~100之间的数,所以后面也无需判断除数是否为零。 
        }
        else 
        {
            j=rand()%4-4;//因为用整型保存,所以使用-4,-3,-2,-1分别表示+,-,*,/ 
            a[i]=j; 
        }
        if(a[i]==-4) printf("%c",'+');
        else if(a[i]==-3) printf("%c",'-');
        else if(a[i]==-2) printf("%c",'*');
        else if(a[i]==-1) printf("%c",'/');
        else printf("%d",a[i]);//输出表达式 
    }
    printf("=
"); 
}
create函数

4.3转化为后缀表达式

        转化为后缀表达式trans()函数:

int trans(int n,int *a)
    {
      int i=0,j=0,top=0;
      int t=a[i++]; //保存当前a[i]的值 
      int b[2*(n-1)];//保存后缀表达式
      int stack[2*(n-1)];//作为栈使用 
      while(i<=2*(n-1))
     {
        switch(t)
        {
            case -4:/*判定为加减号*/
            case -3:
                while(top!=0)
                {
                    b[j]=stack[top];
                    top--;j++;
                }
                top++;
                stack[top]=t;
                break;
            case -2:
            case -1:
                while(stack[top]==-2||stack[top]==-1)
                {
                    b[j]=stack[top];
                    top--;j++;
                }
                stack[++top]=t;
                break;
            default://默认为数字 
                while(t>=0&&t<=100)
                {
                    b[j]=t;j++;
                    t=a[i];i++;
                }
        }
    }
    while(top!=0)
    {
        b[j]=stack[top];
        j++;top--;
    } 
}
    
trans函数

4.4计算后缀表达式

        计算后缀表达式calculate()函数,将第一个数保存在r[3]中。r[1]固定为-1,代表除号,用户每次计算都是取一个操作数与r[0](分子)或者r[2](分母)进行运算;

    //计算后缀表达式,并将结果转化为字符串 
void calculate(int n,int b[],char rs1[]) 
{
    int    i=0,t,top=0;
    int stack[2*(n-1)]={0};
    int r[3]={0,-1,1};//保存最后结果,初始情况下,分子为0,分母为,1 
     
                            //rs1保存最后正确结果的分子 
     char rs2[2*(n-1)]={' '};//最后结果的分母用字符串表示 
       t=b[i];i++;
    while(i<=2*(n-1))
    {
        switch(t)
        {
          case -4:r[0]=r[0]+stack[top]*r[2];top--;break;//+
          case -3:r[0]=stack[top]*r[2]-r[0];top--;break;//-
          case -2:r[0]=r[0]*stack[top];top--;break;//*
          case -1:r[2]=r[2]*stack[top];top--;break;
          default: top++;stack[top]=t;    
        }
         t=b[i++];     
    }
    //化简最终结果
    r[0]=r[0]/gcd(r[0],r[2]);
    r[2]=r[2]/gcd(r[0],r[2]); 
    //将整形转化成字符串 
    itoa(r[0],rs1,10);
    itoa(r[2],rs2,10);
    strcat(rs1,"/");//字符串拼接 
    strcat(rs1,rs2);
    
}
calculate()函数

4.5求最大公约数

        为了化简最后的分数,所以写了一个求最大公约数的函数。

5.运行结果

        运行结果如图:因为之前没有报错,然后就一直在写,只是写的被调函数trans()的测试程序发现闪退,找不到原因,然后想起来用VS测试,但是发现现在来不及了。所以整个代码现在是,没有错,但是跑不出结果的状态,等后面再补充!!!

6.项目小结

        虽然是个很小的项目,再看了其他同学的博客后,很多都说很简单,最开始没有形成具体的思路,所以一下子上手总是卡壳。最开始想的是生成的运算符就用字符串保存,后面自己进行不下去了。后面想着看一下其他同学的代码,从他们规范的定义到用重载,这些或许在老师看来很简单的东西,我一下子都有点懵,现在想想,一个星期这么长时间,自己静下心来,足以把这个东西搞明白,但是我没有想其他同学那样,遇到问题,能静下心来一点一点分析,最后自己把知识点琢磨透彻。于是就按照自己特别low的想法来一步一步写,最后的最后还是出现很多问题,这个程序思路很简单,代码量也很小,但是就是出现没有错误,运行不出来的情况,然后我才想起来没有做测试,于是就单独对每个被调函数测试,发现是转化为后缀表达式trans()函数有问题。后面计算函数依然是这样。慢慢体会到测试的重要性,并且也明白,在编程阶段,不能确保当前代码正确的实现功能的时候,千万不能继续往下赶。还有就是同样时间,自己却没有做出来,学习方法和学习策略确实有问题,下来会和同学多交流,也会继续把这个代码结果跑出来。

原文地址:https://www.cnblogs.com/shuangshuangblog/p/7599952.html