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

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

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

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

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

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

  实现如下:

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;
    }
}

 

posted @ 2016-03-26 19:47  -竹-  阅读(176)  评论(0编辑  收藏  举报