软件工程第3次作业—四则运算(结对作业)

作业要求的博客链接:https://edu.cnblogs.com/campus/nenu/2016CS/homework/2266

git仓库地址:https://git.coding.net/pipifan/f4.git

 本次作业是结对作业,我的结对伙伴是樊友朋同学,他的博客地址是:http://www.cnblogs.com/pipifan/p/9918250.html

 项目概要:

  本次项目实现的是一个用于四则运算的控制台程序,目前已实现的功能如下:

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

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

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

 

项目详情:

一、本次作业采用c/c++进行编程。首先分析项目要求概括功能:

  1)按照控制台输入的N ,生成N道由随机生成的整数合法运算符组成的四则运算,并判断用户输入答案的对错。(输入 f4 -n N)

  2)按照控制台输入的N ,生成N道由随机生成的整数小数合法运算符括号组成的四则运算,并判断用户输入答案的对错。(输入 f4 -c N )

  3)按照控制台输入的N 、文件路径M,生成N道由随机生成的整数小数合法运算符括号组成的不重复的四则运算,给出答案打印在控制台输出到文件M中。(输入 f4 -c  N  -f M)

  4)如果输入的N不合法会输出相应的警告。

二、根据分析规划函数模块:

   1)字符串转化为数字。

   2)判断运算符号的优先级。

   3)处理四则运算表达式,计算表达式的值。

   4)处理控制台输入输出,产生随机数,随机运算符号和随机括号。

   5)主函数,负责调用各个模块,控制流程。

三、部分模块代码实现过程:

  1)处理四则运算表达式。

首先分配两个栈sp,sv,分别存储运算符和运算数字,从左到右读取中缀表达式,遇到操作数就入栈sv,遇到左括号或者当前运算符比sp栈顶优先级高的符号就入栈sp,遇到比sp栈顶优先级低的符号就出栈两个操作数和栈顶操作符,结果再入栈sv,遇到右括号同时栈顶是左括号就出栈左括号。最后sv栈顶就是该表达式的结果。

具体代码如下:

double solve(string s )
{
    stack<double> sv;
    stack<char> sp;
    char c;
    int k = 0, flag = 1;
    double x, y;
    sp.push('');
    c = s[k];
    while (flag)
    {
        if (c >= '0'&&c <= '9' || c == '.') {
            sv.push(toNum(s, k));
        }
        else if (c == ''&& sp.top() == '') {
            flag = 0;
        }
        else if (c == '(' || (priority(c) > priority(sp.top()))) {
            sp.push(c);
            k++;
        }
        else if (c == ')'&& sp.top() == '(') {
            sp.pop();
            k++;
        }
        
        else if (priority(c) <= priority(sp.top())) {
            x = sv.top();
            sv.pop();
            if(sv.empty()) y = 0;
            else {
                y = sv.top();
                sv.pop();
            }
            c = sp.top();
            if( c == '/' && fabs( x ) <= eps ){
                int num = rand() % 3;
                c = mp[num];
            }
            sp.pop();
            switch (c) {
                case '+':y = x + y; break;
                case '-':y = y - x; break;
                case '*':y = x*y; break;
                case '/':y = y / x; break;
            }
            sv.push(y);
        }
        c = s[k];
    }
    return sv.top();
}

   2)随机生成整数数字和运算符。

实现功能一用rand随机生成数字和运算符(提前存到数组中)。

这里在实现过程中有两点我们进行了思考:其一是单纯用rand()会使每次的随机数一样,这样就不符合随机这个概念,所以我们加上 srand( (int)time(0) ),用时间做随机数的种子,这样可以保证每次的随机数不同。其二是考虑到除数不能为0,所以我们判断如果“/”后面的随机数是0就再造数据直至不为0,保证了表达式的正确性。

s = "";
        if( flag == 1 ){
            for(int i = 0 ; i < 7 ; i++ ){
                if( (i % 2) == 0 ){
                    int num = rand() % 10;
                    int len = s.size();
                    while( len > 0 && num == 0 && s[len - 1] == '/' )
                        num = rand() % 10;
                    stringstream ss;
                    ss << num;
                    string tmp;
                    ss >> tmp;
                    s += tmp;
                }
                else{
                    int num = rand() % 4;
                    s += mp[num];
                }
            }
        }

    3)随机生成带括号的整数和小数的数据。

生成小数时用两个随机数相除。

这里的难点是随机加括号,我们采取的办法是先随机加左右括号,最后检查是否匹配,缺少匹配的括号就在表达式首尾加上相应的括号。

for(int i = 0 ; i < 7 ; i++ ){
                string tmp;
                if( (i % 2) == 0 ){
                    int num = rand() % 10;
                    if( (num % 3) == 0 && num > 0 ){
                        int num1 = rand() % 100;
                        double tmp_num = num1*1.0 / 10;
                        stringstream ss;
                        ss << tmp_num;
                        ss >> tmp;
                        x[xx++] = tmp_num;
                    }
                    else{
                        int len = s.size();
                        while( len > 0 && num == 0 && s[len - 1] == '/' )
                            num = rand() % 10;
                        stringstream ss;
                        ss << num;
                        ss >> tmp;
                        x[xx++] = num*1.0;
                    }
                    if( num < 3 && i < 6 && i > 0 ){
                        s += "(";
                        cnt1++;
                    }
                    s += tmp;
                    if( num > 6  && i > 0 && i < 6){
                        if( cnt1 > 0 )
                            cnt1--;
                        else
                            cnt2++;
                        s += ")";
                    }
                }
                else{
                    int num = rand() % 4;
                    s += mp[num];
                }
            }

四、测试阶段:

     1)功能1

 

     2)功能2

    3)功能3

五、项目进行过程

1.给出结对编程的体会  

结对编程的好处是可以集思广益,尤其针对本次作业的一些小的细节可以做到查漏补缺。

比如输入参数错误时的文字提示,功能一和功能二的提示是不同的;比如除数不能为0;比如保留小数的位数时要考虑是否为有限小数,无限小数要保留三位;比如功能三输出时要对齐......等等的一些问题。在解决功能二中如何随机加括号,并且如何匹配正确时,两个人也进行了各自的思考然后加以讨论得出解决方案。在查找相应的参考资料时也更便捷。

由于结对编程需要共用一个设备,所以编程以平时写代码能力比较强的樊友朋同学为主,相当于Driver,我负责收集整理资料、提供思路,发现、修改细节问题和测试数据,相当于Navigator。我对结对编程的体会应该是分清主次,各司其职,需要默契,在讨论中解决问题和完善项目。在《构建之法》中也提到过结对编程的好处,“结对能带来更多的信心,高质量的产出可以带来更高的满足感。”,通过这次结对作业,确实感受的优秀的队友带来的不同的合作感,更加有利于问题的解决。

2. 至少3项在编码、争论等活动中花费时间较长,给你较大收获的事件。

  1). 用栈处理四则运算表达式。具体思路可以参考3.1

  2). 随机生成小数和括号。主要的难点是随机加括号,并且要保证括号的正确性。具体思路参考3.3

  3). 功能二和三要求表达式不重复。初看作业要求中的不重复概念是要运用交换律、结合律等数学定理来检测题的不重复性,但是这样比较麻烦。所以经过讨论后,我的伙伴提出为了保证题的不重复,可以检查每个题中的数字是否相同。因为题目本身是由随机数、随机括号、随机运算符组成的,它重复的概率很低,所以我们把表达式的数字排序,包装放在set里面,检查数字重复时就重新生成表达式,这样不会影响随机性,也不会有重复的表达式,最重要的是降低算法复杂度(相对检查交换律的那种算法)

  4). 花费时间长的地方还有5.1提到的细节问题,为了尽力满足作业要求,我们用了大概两至三个小时去修改细节。

六、给出照片1张,包括结对的2位同学、工作地点、计算机,可选项包括其他能表达结对编程工作经历的物品或场景。

原文地址:https://www.cnblogs.com/kongwy/p/9919850.html