结对项目进展第二周——模块化分析

结对项目第一步:把实现的四则运算程序的功能划分模块,将不同模块功能分开,从而使模块可复用,并作为独立的部分进行测试。

优化的四则运算程序需要高内聚和低耦合。而我们组写得代码使用了树的数据结构,虽然表达起来简单易懂,算法也比较容易实现,却有着一个很大的缺点:我们在递归生成运算表达式的同事计算了表达式的结果。这样虽然算法和实现的代码都很简便,却不符合模块化的思想。

我们选择的原型程序,一共有输入、随机生成表达式框架(只有运算符但没有数据)、在框架中填入数字并计算结果、输出共四个模块。实现的时候觉得很简单,还以为自己选择了一种不错的算法。不过现在也觉得这种算法不错,起码很新颖,但是基于可复用性强的原则来看,模块的划分就太不合理了,并且不符合低耦合高内聚的要求。根据讨论,我们决定将以前程序的主模块再细分为两个模块,分别为自动生成表达式模块和根据表达式求解模块。总之,就是将以前的模块拆开,然后重组为新的模块。

对于自动生成表达式模块,我们现有的代码还是有很强的实用性的。面临的问题就是:如何根据表达式求解。我们一致认为不应该抛弃原来的数据结构,所以新的模块功能设置为:先获得用户输入的表达式字符串,然后将字符串转换成树结构。

接下来面临的问题是操作数的多样性,首先要将操作数字符串转换为相应的数据类型。

实现如下:

void CharsToInt(char *tmpstr,int flag1, int flag2, int * numer, int * demon,int i)
{
    //根据flag1判断为带分数还是真分数
    int j;
    if (flag1 != 0)
    {
        //带分数中的整数部分
        char tmpstr1[10];
        int tmpint1,tmpint2,tmpint3;
        for (j = 0; j < flag1; j++)
        {
            tmpstr1[j] = tmpstr[j];
        }
        tmpstr1[j] = ‘\0‘;
        tmpint1 = atoi(tmpstr1);
        //带分数中的分子部分
        for (j = flag1+1 ; j < flag2; j++)
        {
            tmpstr1[j - flag1 - 1] = tmpstr[j];
        }
        tmpstr1[j - flag1 -1] = ‘\0‘;
        tmpint2 = atoi(tmpstr1);
        //带分数中的分母部分
        for (j = flag2+1; j < i; j++)
        {
            tmpstr1[j - flag2 -1] = tmpstr[j];
        }
        tmpstr1[j - flag2 -1] = ‘\0‘;
        tmpint3 = atoi(tmpstr1);
        *(numer) = tmpint1 * tmpint3 + tmpint2;
        *(demon) = tmpint2;
    }
    else
    {
        char tmpstr1[10];
        for (j = 0; j < flag2; j++)
        {
            tmpstr1[j] = tmpstr[j];
        }
        tmpstr1[j] = ‘\0‘;
        *(numer) = atoi(tmpstr1);
        for (j = flag2 + 1; j < i; j++)
        {
            tmpstr1[j - flag2 -1] = tmpstr[j];
        }
        tmpstr1[j] = ‘\0‘;
        *(demon) = atoi(tmpstr1);
    }
}

下一步:由表达式字符串生成树

实现函数中用到了两个栈:操作数栈&运算符栈

从表达式第一位开始读取字符,如果读到的字符不是运算符,那么就是操作数(错误的情况单考虑),操作数有分数和整数。

比如操作数为11‘2/3,这是一个带分数,现在读取到的是‘1‘,根据这个子字符串得到操作数的分子numer,分母demon。在读取的过程中,用两个标志位表示符号 ’ 和符号 /的位置。然后用CharsToInt()将这个子字符串变成Value类型的操作数,即operation.numer = 35,operation.demon = 3.

  如果读取的是运算符,还要分运算符是否为括号。为左括号进栈,为右括号则脱括号。

void Experssion::GenerateTree(string expr)
{
    stack <char> operStack;//运算符栈
    stack <Experssion *> dataStack;//数据栈
    expr += ‘\0‘;
    char tmpchar,c;
    int idx = 0;
    tmpchar = expr[idx++];
    Experssion * p;
    while (operStack.size() != 0 || tmpchar != ‘\0‘)
    {
        //如果不是运算符,则接收操作数,这个操作数可能为假分数,真分数,和整数。
        if (tmpchar != ‘\0‘ && !IsOper(tmpchar))
        {
            int flag1 = 0;//是否为假分数的标志,为0,不是;不为0,是假分数,且这个数值表示假分数中" ‘ "的位置
            int flag2 = 0;//是否为分数的标志,为0,不是;不为0,是分数,且这个数值表示分数中"/"的位置
            char tmpstr[10];//暂时保存操作数字符串
            int numer,demon;//操作数分子分母
            int i = 0;
            while (tmpchar != ‘\0‘ && !IsOper(tmpchar))
            {
                tmpstr[i] = tmpchar;
                if (tmpchar == ‘\‘‘)//如果字符为" ‘ "
                {
                    flag1 = i;
                }
                if (tmpchar == ‘/‘)
                {
                    flag2 = i;
                }
                tmpchar = expr[idx++];
                i++;
            }
            tmpstr[i] = ‘\0‘;
            //处理这个暂存的字符串,获得分子分母
            if (flag1 == 0 && flag2 == 0)//为整数
            {
                demon = 1;
                numer = atoi(tmpstr);
            }
            else//为分数
            {
                CharsToInt(tmpstr,flag1,flag2,&numer,&demon,i);
            }
            Value operation(numer,demon);
            p = new Experssion();
            p->Result = operation;
            dataStack.push(p);
        }
        else//读取的是运算符
        {
            switch(tmpchar)
            {
            case ‘(‘:
                operStack.push(‘(‘);
                tmpchar = expr[idx++];
                break;
            case ‘)‘:
                while(true)
                {
                    c = operStack.top();
                    operStack.pop();
                    if (c == ‘(‘)
                    {
                        break;
                    }
                    p = new Experssion();
                    p->oper = c;
                    if (dataStack.size())
                    {
                        p->right = dataStack.top();
                        dataStack.pop();
                    }
                    if (dataStack.size())
                    {
                        p->left = dataStack.top();
                        dataStack.pop();
                    }
                    dataStack.push(p);
                }
                tmpchar = expr[idx++];
                break;
            default:
                if (operStack.size() == 0 || tmpchar != ‘\0‘
                    && OperLevel(operStack.top()) < OperLevel(tmpchar))
                {//进栈
                    operStack.push(tmpchar);
                    tmpchar = expr[idx++];
                }
                else
                {//出栈
                    p = new Experssion();
                    p->oper = operStack.top();
                    if (dataStack.size())
                    {
                        p->right = dataStack.top();
                        dataStack.pop();
                    }
                    if (dataStack.size())
                    {
                        p->left = dataStack.top();
                        dataStack.pop();
                    }
                    dataStack.push(p);
                    operStack.pop();
                }
                break;
            }
        }
    }
    p = dataStack.top();
    dataStack.pop();
    this->oper = p->oper;
    this->left = p->left;
    this->right = p->right;
}

最后根据生成的表达式树求解,采用递归的方法:

//由生成的表达式树求解
Value Experssion::GetResult()
{
    if (left != NULL && right != NULL)
    {
        Value LResult = left->GetResult();
        Value RResult = right->GetResult();
        switch(oper)
        {
        case ‘+‘:
            Result = LResult + RResult;
            break;
        case ‘-‘:
            Result = LResult - RResult;
            break;
        case ‘*‘:
            Result = LResult * RResult;
            break;
        case ‘>‘:    //“>”代替除号
            Result = LResult / RResult;
            break;
        }
        return Result;
    }
    else
    {
        return Result;
    }
}
时间: 2024-10-12 21:45:11

结对项目进展第二周——模块化分析的相关文章

20172327 结对编程项目-四则运算 第二周 阶段总结

20172327 结对编程项目-四则运算 第二周 阶段总结 结对编程项目-四则运算 第二周 输出阶段总结博客 结对对象: 学号:20172317 姓名:蒋子行 伙伴第二周博客地址: 学号:20172320 姓名:李闻洲 伙伴第二周博客地址: 担任角色: 驾驶员:蒋子行 副驾驶:李闻洲 马瑞蕃 小组结对编程的photo: 项目中自己负责的部分: 我在项目中真的没啥贡献,项目主要由祥哥主刀,我和李闻洲就是打杂的. 个人贡献度划分: 我在项目中真的没啥贡献,项目主要由祥哥主刀,我就是打杂的,提点建议和

20175311胡济栋 2018-2019-2《Java程序设计》结对编程项目-四则运算 第二周 阶段性总结

20175311胡济栋 2018-2019-2<Java程序设计>结对编程项目-四则运算 第二周 阶段性总结 需求分析 这是利用栈来设计一个计算器的第二阶段总结. 自动生成四则运算的题目(加.减.乘.除) 需要实现计算结果的输出 将正确的计算结果与用户输入的计算结果进行比较得出最终的正确率 之前编写的程序还有很多问题,这周主要对这些问题进行改进和升级,我们成功实现了这些功能. 设计思路 1. 首先我们需要编写随机生成数字和随机生成符号的代码,把他们编写好后保存 2. 我们需要利用之前编写好的随

20172316 结对编程-四则运算 第二周 阶段总结

20172316 结对编程-四则运算 第二周 阶段总结 1.项目内容 以结对小组形式编写代码,实现以下功能: 1.自动生成题目 2.题目运算(判题) 3.支持真分数 4.生成题目添加括号 5.题目去重(扩展需求,加分项) 6.文件处理(扩展需求,自行学习,加分项) 相关过程截图 最开始的代码名称十分不规范,有的使用拼音,有的使用英文,有的不知所云,其他组员第一次看见名称时不容易理解和衔接起来: 出现了两个程序之间符号表示的差异,对于式子的值的计算有很大影响: 结对评价 唐才铭 王文彬 关键代码解

20175314 结队编程项目——四则运算第二周

20175314 结队编程项目--四则运算第二周 一.需求分析 实现一个命令行程序,要求: 自动生成小学四则运算题目(加.减.乘.除) 支持整数 支持多运算符(比如生成包含100个运算符的题目) 支持真分数 统计正确率 能生成随机数 产生的算式要有括号 要建立堆栈,进行中缀转后缀,以及后续后缀的运算 能输入想要产生的题目数 能输入用户计算的答案 能够比较用户输入的答案是否正确 能够统计用户答题的正确率 二.设计思路 生成一个有加减乘除支持括号的算式,以字符串的形式输出,每个操作数或操作符中间都用

20165330 结对编程项目-四则运算 第二周

需求分析 实现一个命令行程序,要求:自动生成小学四则运算题目(加.减.乘.除) 可实现多个运算符一起运算 可以真分数形式输出结果 测试结果的正确性 统计题目完成数并计算正确率 设计思路 实验首先是完成一个计算器的功能,可以实现简单的+.-.*./运算,在这里设计一个主类生成随机数,生成题目,并判断正确率 实现多运算符,编入四个类分别实现整数运算.真分数运算.判断结果正确并计算正确率 利用JUnit检测非法输入 设计测试类,利用JUnit测试整数类与分数类的四则运算 UML类图 功能截图 Juni

团队项目第六周——事后诸葛亮分析(苏察哈尔灿)

总结 我们团队做的是web网页活动订座系统,刚开始讨论的是电影订票系统,但后来觉得太寻常了,于是为了增加可选择性,改成活动订座系统,这样无论什么活动,只要涉及活动订座,都可以使用此系统,具有活动通用性,团队成员觉得都可以.于是便开始进行前后端的分工设计,想好web网页需要分成几个部分,每个人需要负责什么,然后根据团队计划一步步向前推进,最后各成员基本都完成各自的任务,得到的成品虽然还有待提高,但基本满足我们的需求,因为本身就想用这个团队作业练手,所以总体来说还算可以吧.事后分析的话,觉得团队内部

20165202 结对编程项目-四则运算 第二周

一.码云链接 二.需求分析 实现一个命令行程序,要求: 自动生成小学四则运算题目(加.减.乘.除) 支持整数 支持多运算符(比如生成包含100个运算符的题目) 支持真分数 统计正确率 三.设计思路(同时输出UML类图) 这周的总体设计要在上周的基础上深化,上周我们的代码没有用到中缀表达式转后缀表达式,没有用到栈,所以这周不仅要对上一周的代码进行修改,而且还要在此基础上完成更深层的对真分数的要求. 实现四则运算中后缀转换类 实现四则运算后缀表达式计算类 实现四则运算真分数计算类 实现四则运算生成题

个人项目四则运算生成程序进展——第二周

我认为四则运算生成程序功能上分为两大部分,随机生成四则运算表达式+四则运算表达式的计算 开始的时候想从编程结构上划分这两部分,用逆波兰表达式和栈的结构进行四则运算的计算,但是实现效果不太理想,编程到一半就编不下去了. 后来上网查阅了一些资料,并受到<编译原理>书中表达式可以用树的结构来表示的启发,我采用了二叉树的数据结构,并采用了生成表达式和计算表达式同时进行的方法,用递归运算的思想,重新编写了这个程序. 至于生成算式的随机性,则采用了控制表达式中数字个数的方法(叶子数量),来控制表达式的长度

结对项目进展二

经过这一周的结对编程工作,我们的项目取得了很大进展.我进一步体会到了结对编程的优势和劣势.优势是两人合作得当可以提高debug的效率.在队友的帮助下,我可以更快地理清思路,发现我的代码中的错误.提高效率.但是我们在交流一些深入的问题的时候,也要花费额外的时间,这也是劣势所在. 由于我们之前的程序模块划分不是很明确,不符合软件工程中“高内聚,低耦合”的思想,不便于跨平台.这次我们仔细分析了程序的结构,把功能划分成几个独立的模块,封装在类中,模块之间通过接口交换信息. 1.界面类(基于QT中的Mai