• 软件工程第四次作业-3四则运算


    结对同学:姜珊

     

    目录:

    1.需求分析

    2.基本设计

    3.代码说明

    4.测试运行

    5.重难点知识

    6.缺点改进及体会

     

    1.需求分析:

           作业链接:https://edu.cnblogs.com/campus/nenu/SWE2017FALL/homework/997

      功能一和功能二其实可以合并成为一个功能,就是计算带括号的四则运算式,并对结果进行判断和输出。

      功能三是利用输入参数来进行出题并输出。

    2.基本设计

           由需求分析可以得到,可以根据参数个数来执行某一程序段。

           对于功能一二,只有一个参数就是文件名,直接输入f4并回车即可运行。

           对于功能三,要输入三个参数如:f4 –c 2,其中-c后边的数字要当作程序体中的循环次数,完成输出个数的限定。

     

    3.代码说明

           地址:https://git.coding.net/immixiaomi/f4-mhjs.git

           首先定义了字符类型的栈及相关操作,在将中序表达式转化为后序表达式时要用到:

    typedef int Status;
    typedef char ElemType;
    typedef struct 
    {
        ElemType data[MAXSIZE];
        int top;//栈顶指针
    }Stack;
    
    Status InitStack(Stack *S)//初始化
    {
        int i;
        for(i = 0; i < MAXSIZE; i++)
        S->data[i] = NULL;
        S->top = -1;
        return 1;
    }
    
    Status CreateStack(Stack *S,int n)//创建一个长度为n的堆栈
    {
        if(n>MAXSIZE || n<1)
        {
            printf("输入长度有误!
    ");
            return -1;
        }
        srand(time(0));
        int i;
        for(i = 0; i < n; i++)
        {
            S->data[i] = rand()%100+1;
        }
        S->top = n-1;
        return 1;
    }
    
    Status push(Stack *S,ElemType e)//压栈操作
    {
        if(MAXSIZE-1 == S->top)
        {
            printf("栈已满
    ");
            return -1;
        }
        ++(S->top);
        S->data[S->top] = e;
        return 1;
    }
    
    Status pop(Stack *S,ElemType *e)//出栈
    {
        if(-1 == S->top){
            printf("栈为空!
    ");
            return -1;
        }
        *e = S->data[S->top];
        --(S->top);
        return 1;
    }

      GetStr()函数是自动生成字符串的函数,参数是空字符数组,通过随机数的产生,并将随机数用itoa()函数转化成字符型,加入随机的运算符产生了中序表达式。先产生了表达式再向其中加入括号,括号都是成对加入,完成了带括号表达式的构建。

    void GetStr(char *str)//构建随机串,并添加括号 
    {
        char str1[1] = "";
        int a, b, c, d;
        char op1, op2, op3;
        int i;
        srand((int)time(0));
        a = (int)(rand()%10);//四个变量 
        b = (int)(rand()%10);
        c = (int)(rand()%10);
        d = (int)(rand()%10); 
        i = (int)(rand()%4);//三个操作符 
        switch(i)
        {
            case 0:
                op1 = '+'; break;
            case 1:
                op1 = '-'; break;
            case 2:
                op1 = '*'; break;
            case 3:
                op1 = '/'; break;
        }
        i=(int)(rand()%4);
        switch(i)
        {
            case 0:
                op2 = '+'; break;
            case 1:
                op2 = '-'; break;
            case 2:
                op2 = '*'; break;
            case 3:
                op2 = '/'; break;
        }
        i=(int)(rand()%4);
        switch(i)
        {
            case 0:
                op3 = '+'; break;
            case 1:
                op3 = '-'; break;
            case 2:
                op3 = '*'; break;
            case 3:
                op3 = '/'; break;
        }
        itoa(a, str1, 10);
        strcpy(str, str1);
        str[1] = op1;
        itoa(b, str1, 10);
        strcat(str, str1);
        str[3] = op2;
        itoa(c, str1, 10);
        strcat(str, str1);
        str[5] = op3;
        itoa(d, str1, 10);
        strcat(str, str1);
        for(int j = 0; j < 7; j++)
        {
            if(str[j] == '/')
            {
                int e;
                e = (int)(rand()%9)+1;
                itoa(a, str1, 10);
                str[j+1] = str1[0];
            }
        }
        int j, k;//左括号右括号 
        int flag = 0;
        j = (int)(rand()%3);
        int length = 8;
        if(j == 0)//位置1加不加左括号的判定 
        {
            k = (int)(rand()%4);
            if(k != 3&&flag != 2)
            {
                for(int i = length;i > 0;i--)//向坐标0处插入括号 
                {
                    str[i] = str[i-1];
                }
                str[0] = '(';
                flag++;
                length++;
                int l = (int)(rand()%4);
                if(l < 2)
                {
                    for(int i = length; i > 4; i--)
                    {
                        str[i] = str[i-1];
                    }
                    str[4] = ')';
                    flag--;
                    length++;
                    for(int i = length;i > 6;i--)
                    {
                        str[i] = str[i-1];
                    }
                    str[6] = '(';
                    flag++;
                    length++;
                    for(int i = length;i > 10;i--) 
                    {
                        str[i] = str[i-1];
                    }
                    str[10] = ')';
                    flag--;
                    length++;
                }
                else if(l = 2)
                {
                    for(int i = length; i > 4; i--) 
                    {
                        str[i] = str[i-1];
                    }
                    str[4] = ')';
                    flag--;
                    length++;
                }
                else if(l = 3)
                {
                    for(int i = length; i > 6; i--)//插入括号 
                    {
                        str[i] = str[i-1];
                    }
                    str[6] = ')';
                    flag--;
                    length++;
                }
    
            }
            else if(k != 2&&flag != 2)
            {
                for(int i = length+1; i > 1; i--)//向坐标0,1处插入俩左括号 
                {
                    str[i] = str[i-2];
                }
                str[0] = '(';
                str[1] = '(';
                flag = flag+2;
                length = length+2;
                
                for(int i = length; i > 5; i--)
                {
                    str[i] = str[i-1];
                }
                str[5] = ')';
                flag--;
                length++;
                
                for(int i = length; i > 8; i--)
                {
                    str[i] = str[i-1];
                }
                str[8] = ')';
                flag--;
                length++;
            }
        }
        
        else if(j == 1)
        {
            k=(int)(rand()%4);
            if(k != 3&&flag != 2)
            if(0)
            {
                for(int i = length; i > 2; i--)//向坐标2处插入括号 
                {
                    str[i] = str[i-1];
                }
                str[2] = '(';
                flag++;
                length++;
            }
            else if(k != 2&&flag != 2)
            {
                for(int i = length+1; i > 3; i--)//向坐标2,3处插入俩左括号 ,只能接着向6和8(原坐标)添加括号 
                {
                    str[i] = str[i-2];
                }
                str[2] = '(';
                str[3] = '(';
                flag = flag+2;
                length = length+2;
                
                for(int i = length; i > 7; i--)
                {
                    str[i] = str[i-1];
                }
                str[7] = ')';
                flag--;
                length++;
                for(int i = length;i > 10;i--)
                {
                    str[i] = str[i-1];
                }
                str[10] = ')';
                flag--;
                length++;
            }
        }
        else if(j == 2)
        {
            k = (int)(rand()%4);
            if(k != 3&&flag != 2)
            {
                for(int i = length; i>4; i--)//向坐标4处插入括号 
                {
                    str[i] = str[i-1];
                }
                str[4] = '(';
                flag++;
                length++;
                //str4 插入左括号一个
                for(int i = length; i>8; i--)
                {
                    str[i] = str[i-1];
                }
                str[8] = ')';
                flag--;
                length++;
            }
        }
        
        for(i = 0; i < length-1; i++)
        {
            printf("%c",str[i]);
        }
        printf("="); 
    }
    View Code

      Translate()函数是将中序表达式转化为后序表达式,参数是两个字符串,分别是旧串和转化后的新串。用栈进行操作,并对括号进行配对取出,转化成为没有括号的逆波兰表达式。

    int Translate(char *str,char *exp)//中缀表达式转后缀表达式
    {
        //新建一个栈,来存储符号
        char e;
        Stack S;
        if(InitStack(&S) != 1)
        {
            printf("初始化栈失败!
    ");
    
        }
        //当带转换的字符串*mid未终止时,循环处理
        while(*str)
        {
                //如果是数字,则直接输出
            if(*str >= '0' && *str <= '9')
            {
                *(exp++) = *(str++);
                continue;
            }else if(*str == '+' || *str == '-' || *str == '*' || *str == '/' || *str == '(' || *str == ')')
            {
                //输入的是合法运算符号,比较之前是否有更高优先级的符号
                if(S.top == -1 || '(' == *str)
                {
                    //当符号栈为空或遇到左括号时,符号入栈
                    push(&S, *(str++));
                    continue;
                }
                if(')' == *str)
                {
                    //遇到右括号时,栈顶元素依次出栈;直到遇到第一个左括号时结束
                    pop(&S, &e);
                    *(exp++) = e;
                    while(pop(&S, &e) && e != '(')
                    {
                        *(exp++) = e;
                    }
                   // printf("%c
    ",e);
                    str++;
    
                    continue;
                }
                //后续的处理都要取出临时的栈顶元素,与当前输入的符号*mid相比较;当临时栈顶元素优先级大于等于输入符号的优先级时,出栈;否则符号入栈(已经弹出一个,记得把弹出的元素也入栈)
                pop(&S,&e);
                if('+' == *str || '-' == *str)
                {
                    if(e == '(')
                    {
                        push(&S, '(');
                        push(&S, *(str++));
                        continue;
                    }
                    else
                    {
                        *(exp++) = e;
                        push(&S, *(str++));
                        continue;
                    }
                }
                else if('*' == *str || '/' == *str)
                {
                    if('*' == e || '/' == e)
                    {
                        *(exp++)=e;
                        push(&S, *(str++));
                        continue;
                    }
                    else
                    {
                        push(&S, e);
                        push(&S, *(str++));
                        continue;
                    }
                }
    
            }
            else
            {
                printf("error%c
    ", *str);
                return -1;
            }
        }
        //当待转换的字符已经结束时,符号栈至少还有一个元素(中缀表达式的特点:数字结尾;后缀表达式以符号结尾);将栈中的元素依次出栈
        while(S.top != -1)
        {
            pop(&S, &e);
            *(exp++) = e;
        }
        //字符串的结束符!
        *exp = '';
    }

      Calculate()函数是将后缀表达式进行计算,返回值类型为double型,值为该表达式的结果。

    double Calculate(char* exp)//计算逆波兰表达式的值 
    {
    
        int i = 0;
        double temp[MAXSIZE];
        int top = -1;
        double a;
        while(exp[i] != '')
        {
            if(exp[i]>='0' && exp[i]<='9')
            {
                temp[++top] = exp[i]-'0';
            }
            else if(exp[i] == '-')
            {
                double m = temp[top];
                top--;
                double n = temp[top];
                top--;
                temp[++top] = n-m;
                
                
            }
            else if(exp[i] == '+')
            {
                double m = temp[top];
                top--;
                double n = temp[top];
                top--;
                temp[++top] = n+m;
            }
            else if(exp[i] == '/')
            {
                double m = temp[top];
                top--;
                double n = temp[top];
                top--;
                temp[++top] = n/m;
            }
            else if(exp[i] == '*')
            {
                double m = temp[top];
                top--;
                double n = temp[top];
                top--;
                temp[++top ] = n*m;
            }
            i++;
        }
    
        return temp[top];
    }

      输出有两个函数,PrintOnScr()负责功能一二的输出,PrintOnFile()负责功能三的输出,但是功能三因技术原因未完成,暂时只输出至屏幕。

    void PrintOnScr(double answer,int count)//输出至屏幕,对应功能一二 
    {
        printf("
    ");
        printf("?");
        double ans;
        scanf("%lf",&ans);
        if(answer == ans)
        {
            printf("答对啦,你真是个天才!");
            count++;
            //break;
        }
        else
        {
            printf("再想想吧,答案似乎是%d喔!",answer);
        }
        printf("
    ");
    }
    
    
    void PrintOnFile(double answer)//输出至文件,对应功能三 
    {
        printf("			%d
    ",answer);
        return;
    }

      主函数里定义两个空串,用于传入函数中接收数据,然后根据参数个数执行对应某段函数体。

    int main(int argc, char *argv[])
    {
        char str[MAXSIZE]="";
        char final[MAXSIZE]="";
        double answer;
        int count = 0;
        if(argc == 1)
        {
            
            for(int i = 0; i < 20; i++)
            {
                GetStr(str);
                Translate(str,final);
                answer=Calculate(final);
                PrintOnScr(answer,count);
                fflush(stdin);
            }
            printf("
    ");
            printf("你一共答对%d道题,共20道题。", count); 
            return 0;    
        }
        else if(argc == 3)
        {
            int n = atoi(argv[2]);
            if(n < 0)
            {
                printf("题目数量必须是 正整数。");
            }
            else if(argv[2][0] >= 'a'&&argv[2][0]<='z')
            {
                printf("题目数量必须是 正整数。");
            }
            else 
            {
                for(int i = 0; i < 100; i++)
                {
                    for(int j = 0; j < 100; j++)
                    {
                        if(argv[i][j] == '.')
                        {
                            printf("题目数量必须是 正整数。");
                            return 0;
                        }
                    }
                }
                
                for(int i = 0; i < n; i++)
                {
                    GetStr(str);
                    Translate(str,final);
                    answer=Calculate(final);
                    PrintOnFile(answer);
                }    
            }
        }
    }

    4.测试运行

      功能一和功能二:

      只能算最多2道题,然后程序崩溃了。我估计是堆栈溢出的错误,但是不知如何清空内存,因为在刚刚打开窗口的时候程序是正常的。无法修改这个错误。

      功能三:

      可以正确判断-c后边的参数,却无法进行输出,程序再次崩溃。

    5.重难点知识

      本次编程用到了以下知识,参考链接如下:

      C语言产生随机数:http://www.cnblogs.com/qjziyou/p/4782454.html

      Itoa函数:http://www.cnblogs.com/bluestorm/p/3168719.html

      定义字符栈的方法:http://blog.csdn.net/keepupblw/article/details/26343769

      判断整数:http://blog.csdn.net/youngdze/article/details/13628877

      浮点数转换成ASCII码:http://download.csdn.net/download/letre/1936905

     

    6.缺点改进及体会

           这次的三个功能都没有完全实现,我和姜珊同学对需求的考虑不充分,没有提前测试一下循环情况下程序会不会溢出,结果真的发生了溢出,也尝试过修改但是并没有奏效。在字符栈那里的使用肯定没有想象的那么顺利。最开始我们一起讨论代码规范的书写,互相交流了风格,发现了我们其实并没有什么风格,就制订了一份详细的规范我们仔细遵守就好。此外,我和姜珊同学对于函数的参数设置进行了争论,最后一致决定采用字符串传参数。在Calculate()函数这里我们讨论了一下是否要使用atoi()函数,我认为采用减去’0’的效果更好更简洁,最后我们决定使用后者的方法。在遇到字符栈输出溢出的时候,我们试着重新写了一个新的函数,遗憾的是,效果还没有原来的好,根本不能运行,直接崩溃。在最后判断浮点数的时候,我提出了将其转化为ASCII码的建议,姜珊同学认为直接搜索小数点即可,我们采用了她的方法。

           以后要注意对代码段的掌控,不要等到错误无法挽回在试图修改。

      照片:

  • 相关阅读:
    外部无法捕捉Realm的doGetAuthenticationInfo方法抛出的异常
    Redis多实例及主从搭建
    Redis安装
    window.showModalDialog两次加载问题清除缓存方法
    JQuery分页插件bs_pagination的应用
    解决IE11出现异常SCRIPT5011:不能执行已释放Script的代码
    通过CSS禁用页面模块的复制和粘贴功能
    C# toString()转换详细(转)
    百度ue富文本编辑器setContent方法报错初始化加载内容失败解决办法
    前端页面div float 后高度 height 自适应的问题
  • 原文地址:https://www.cnblogs.com/immixiaomi/p/7650450.html
Copyright © 2020-2023  润新知