• 软件工程第3次作业


    要求0:

    作业地址:https://edu.cnblogs.com/campus/nenu/2016CS/homework/2266

    要求1:

    git地址:  https://git.coding.net/Jingr98/f4.git

    要求2:

    1.结对编程同学的博客地址&姓名:https://www.cnblogs.com/Jingr/p/9924521.html  井冉

    2.解题思路:

      当初看到四则运算这个题目时,第一想法是先解决计算表达式这一块,再来增加生成表达式的功能,大致形成计算表达式(满足功能1)-->计算表达式(满足功能2和功能3)-->随机生成表达式的流程思路。于是先在网上查找了四则运算计算这一块的内容,发现大多会使用中缀表达式与后缀表达式来完成,并采用双栈(即操作数一个栈,运算符一个栈),从这篇博客我还了解到挺多的(https://blog.csdn.net/that163/article/details/7558268),代码简短易懂。其次就是随机生成四则运算题目,在网上查阅了很多博主写的代码,参考了他们的随机生成算式的思路如下:

    1. 随机生成一个数字,代表着生成表达式中操作数的个数。
    2. 循环生成一个数字,将其输出,然后等概率生成‘+’‘-’‘*’‘/’中的一个跟在该数字后面,输出。
    3. 以一定概率生成左括号,若生成了左括号则输出,并进行计数标志当前共有多少个未完成匹配的左括号。
    4. 若当前有未完成匹配的左括号,则在生成一个数字后,生成一个操作符前,以一定的概率生成右括号。
    5. 在生成完毕后,生成最后一个数并将为匹配的左括号予以匹配。

    附上博客地址,希望对大家有用(https://www.cnblogs.com/fanfan-blogs/p/5246469.html)。

    3.重点/难点

    1)功能1:支持整数和不含括号的四则运算且表达式可以重复。

      功能1只需要计算整数,所以唯一的难点是输出结果时的判断。当时没有读清题目,考虑不仔细,简单的判断若为整数,则原样输出,否则,保留3位小数,但发现会存在有可以被整除的小数,得到的结果可能是类似2.5,19.36等这样的形式,这时保留3位小数就会输出结果冗余。所以又改了思路,判断小数点后面的位数,如果位数超过3位,则保留3位输出,否则原样输出。附上判断小数位数代码。

    函数point():判断小数点后面的位数是否超过3

    /**
    *函数名:point()
    *函数功能:判断小数点后面的位数是否超过3
    *param:double num
    */
    int point(double num)
    {
        int i,f = 0;
        num *= 1000;
        if(num - (int)num > eps)
            f = 1;
        return f;
    }

    2)功能2:支持小数和含小括号的四则运算且表达式可以重复。

      功能2涉及到小数和小括号的运算,同样也涉及到了结果的输出以及和用户的结果进行对比,判断是否正确。由于要求只需保留到3位小数,故设置eps=1e-4,当用户结果与正确答案相差小于eps时,则判定回答正确。其次,是由于小括号的加入,所以还涉及到了更进一步的优先级计算,于是直接设置了判断优先级的函数,给6种运算符赋予优先级。最后就是由于小括号的加入,需要随机生成小括号,下附部分代码:

    //小括号的随机生成
                if( n < 3 && i < 6 && i > 0 ){
                    s += "(";
                    ans1++;
                }
                s += tmp;
                if( n > 6  && i > 0 && i < 6){
                    if( ans1 > 0 )
                        ans1--;
                    else
                        ans2++;
                    s += ")";
                }
            } else {
                int n = rand() % 4;
                s += op[n];
            }
        }
        sort( a , a + 4 );
        pdd current = mk( a[0] , a[1] , a[2] , a[3] );
        if( stPdd.find(current) != stPdd.end() ){
            tmp++;
            return 1;
        }
        stPdd.insert( current );
        string lft , rht;
        //保证小括号成对出现
        for(int i = 0 ; i < ans2 ; i++ )
            lft += "(";
        for(int i = 0 ; i < ans1 ; i++ )
            rht  += ")";
        s = lft + s + rht;

    3)功能3:表达式不重复且输出结果显示在控制台,且将控制台显示的结果输出到指定位置的txt文件中。

      功能3其实就是在功能2的基础上增加表达式不重复且将结果输出到指定文件中的功能。在使生成的表达式不重复上,我感觉还是挺难的,因为你需要判断交换律、结合律、分配律等导致的重复的情况,比较复杂,所以这个地方是参考了别人的代码,然后是将控制台结果输出到txt文件里,在这里运用到了C++的ofstream,

      ofstream fout( "d:\mytest.txt" );

      fout << "Learning C++ is very useful."<< endl;

    搜索到了这样一篇博客,详细的讲解了ofstream与ifstream的用法(https://www.cnblogs.com/batman425/p/3179520.html)。

    4)解决计算表达式的功能:利用双栈将中缀表达式转换成后缀表达式,遍历后缀表达式得到结果。此处通过双栈,分离开操作数与运算符,简化了计算,方便操作与判断。下附解决代码:

    /**
    *函数名:work()
    *函数功能:利用双栈将中缀表达式转换成后缀表达式,遍历后缀表达式得到结果
    *param:string s
    */
    double work(string s )
    {
        stack<double> sNum; //存储操作数的栈
        stack<char> sOp;    //存储运算符的栈
        int i = 0, flag = 1;
        char c;
        double x, y;
        sOp.push('');
        c = s[i];
        while (flag)
        {
            if (c >= '0' && c <= '9' || c == '.') {
                sNum.push(TransToNum(s, i));
            }
            else if (c == '' && sOp.top() == '') {
                flag = 0;
            }
            else if (c == '(' || (power(c) > power(sOp.top()))) {
                sOp.push(c);
                i++;
            }
            else if (c == ')'&& sOp.top() == '(') { //遇到一对括号,先将括号内算式进行计算
                sOp.pop();
                i++;
            }
            else if (power(c) <= power(sOp.top())) {
                x = sNum.top();
                sNum.pop();
                if(sNum.empty())
                    y = 0;
                else {
                    y = sNum.top();
                    sNum.pop();
                }
                c = sOp.top();
                if( c == '/' && fabs( x ) < eps ){
                    int n = rand() % 3;
                    c = op[n];
                }
                sOp.pop();
                switch (c)
                {
                    case '+':y = x + y; break;
                    case '-':y = y - x; break;
                    case '*':y = x * y; break;
                    case '/':y = y / x; break;
                }
                sNum.push(y);
            }
            c = s[i];
        }
        return sNum.top();
    }

    4.编程收获:

      这次编程题目看似只是简单地四则运算,但是由于涉及到众多要求,后期改善时发现了一些bug,比如小数位数的判断与处理,重复表达式的处理等等。从前期的思路搭建,到实际代码编写的落实再到后期bug调试解决,都让我感觉到前期的仔细规划有多么重要。这次也是回到了基本的C++语言来编写,在查找参考他人的代码时,会碰到一些以前从未使用过的C++11标准与STL库等,通过对这些知识的学习,感觉自己对于C++的理解与掌握又更深了一步。

    5.结对编程的体会:

      这次的结对编程是两人一起完成一个程序,在编写代码的过程中,一个人充当驾驶员,另一个人充当领航员,在这种模式下,当一方出现代码编写的bug时,另一方就会及时提醒,使其及时更正,与个人编程相比,结对编程一定程度上减少了很多不必要的bug的后期调试时间,当一方较为疲累是,也能相互鼓励监督,提高了编程效率。

      结对编程有利有弊,在一定程度上他是限制了写代码的自由,因为你需要考虑你的搭档的情况,但他又有着比个人编程更为优势的一点是你能接触到不同的思路,可以开拓自己的思维。总而言之,只要双方掌握好交流的度,互相尊重,结对编程能达到事半功倍的效果。

    6.3项在编码、争论等活动中花费时间较长,给我较大收获的事件

    1)初期解题思路发生分歧

    起初我的思路是先解决计算表达式问题,再解决随机生成表达式问题,而我的搭档则恰好相反,两相商讨下,我们最后采用了先计算再生成的思路。由于解题思路商定费了一定的时间,加之一人查阅相关资料,另一人编写代码,刚开始双方步调都不太能协调的上,所以在编写代码前就费时不少。

    2)编写代码时功能2的实现

    功能2加入了小数和小括号计算,与之对应的题目生成就也必须加上小数和小括号的随机生成,这就与功能1相比上升了不少难度。我们两人在编码时就不知道该如何去解决这个问题,两人在网上查找资料,互相讨论,总结资料上的思路,最后是通过随机生成的2个整数相除来生成整数与小数,小括号则是利用对称保证它一定是成对出现的。

    3)编写代码时功能3的实现

    功能3需要判断表达式不重复,以及控制台结果输出到文本。后者还较为简单,但前者就比较复杂,讨论时我们一开始想着判断数字是否相等,以及数字间的运算符是否相等,但发现这种方法判断起来耗时速度慢,操作也比较复杂,查阅相关资料,参考其他同学的思路,发现只要判断数字是否重复即可,于是将数字存储到set集里,利用sort函数排序在比较是否有重复,以实现这一功能。

    7.照片

  • 相关阅读:
    【转】测试人员职业规划
    phantomjs处理alert、confirm弹窗
    linux搭建phantomjs+webdriver+testng+ant自动化工程
    linux搭建apache服务并修改默认路径
    linux环境vnc部署过程详解
    mongodb集群+分片部署(二)
    mongodb部署单节点(一)
    java javaScript实现遮罩层 动态加载
    感受
    JavaScript之中Array用法的一些技巧总结
  • 原文地址:https://www.cnblogs.com/zhangxlBlog/p/9926518.html
Copyright © 2020-2023  润新知