• 面向对象程序设计_Task4_Calculator1.1


    The 2nd part of the Calculator program

    题目链接:Click Here
    github链接:Click Here

    诶嘿,第二部分,要开始实现计算的功能了,估计离不是黑框框的界面也不远了(离题ing...)

    Part 1

    话归正传,这一次的目的是在cmd(我的是windows)调用的方式来得出计算的表达式的值,所以,在函数功能基本完工之后,需要将main函数的参数进行调整,即 int main(int argc, char argv[]) ,因为我使用的是VS,所以一开始就加上main函数的参数,然后在项目属性-调试-参数直接输入要计算的表达式即可。

    Part 2

    然后就是Calculation类的实现啦

    其实一看到这个,我第一感觉就是用前后缀表达式来做,但是仔细想想好像又有点不符合题目的要求:

    “要求使用两个栈来处理(这里要使用):一个存放数值,一个存放符号。从队列中读取字符串,并分别放入对应的栈中。”

    这一点似乎与后缀表达式的过程有点矛盾,后缀表达式的处理结果是某一个栈存放着顺序或逆序的后缀表达式,而不是一个栈存放着数值,另一个栈存放着符号。

    拖着这么久没写的原因之一,是一直有个想法,根据后缀表达式的计算顺序,将后缀表达式拆分成数值和符号两部分,然后参照后缀表达式的计算思想扫描两个栈进行计算,但是....革命尚未成功,同志仍须努力...

    所以最后还是暂时先用后缀表达式来计算。

    OK,那么什么是后缀表达式呢?将将将将~

    我们习惯上写的表达式,成为中缀表达式,如 (1 + 2) * 5 - 6

    而前后缀表达式与之不同之处在于,运算符相对于操作数的位置不同:前缀表达式的运算符位于与其相关的操作数之后,后缀同理

    如上式的前缀表达式:- * + 3 4 5 6
    后缀表达式: 3 4 + 5 * 6 -

    讲完了概念,接下来就是怎么转换和求值——参考博客

    了解完前中后缀表达式,下面便是实现过程了,为了便于理解,version1.1.0(hah~)用了3个栈来操作

    首先是Calculation.h头文件的主要部分


    const int SIZE = 250;     // 表达式的最大长度(即q.size() <= SIZE)
    
    class Calculation
    {
    public:
        Calculation(void);
        ~Calculation(void);
        void setPriorityLevel();
        bool isOperator(string s);
        bool isDigit(string s);
    	void toPostfixExpression(queue<string> q);
        void calculatingExpression(queue<string> q, bool is_Exceed10);
    public:
        int priority[128];     // 存储运算符的优先级
        stack<string> cacheStack;     // 缓存栈
        stack<string> digitStack;     // 数字栈,包括处理与运算过程
        stack<string> operatorStack;     // 操作符栈,包括"+", "-", "*", "/", "(", ")"
    };
    

    对这一个类主要的函数 void toPostfixExpression(queue q); 以及 void calculatingExpression(queue q, bool is_Exceed10); 的解析:

    toPostfixExpression函数是将传入的队列转换成后缀表达式,依照转换的思想并不难写出:解决了对数值、运算符(暂只支持+-*/)、左右括号的不同处理,其他再慢慢调整即可。


    void Calculation::toPostfixExpression(queue<string>q)
    {
        string temp;     // 记录传入的q队列的队首元素
        while (!q.empty())
        {
            temp = q.front();
    
            if (isDigit(temp))     // 若该队首元素是数字,则直接进栈
            {
                cacheStack.push(temp);
            }
    
            else if (isOperator(temp))     // 若该队首元素为运算符,包括"+", "-", "*", "/"
            {
                if (operatorStack.empty())     // 若运算符栈为空,则直接进栈
                {
                    operatorStack.push(temp);
                }
                else if (operatorStack.top() == "(")     // 若运算符栈顶为左括号,则直接进栈
                {
                    operatorStack.push(temp);
                }
                else if (priority[temp[0]] < priority[operatorStack.top()[0]])     // 若当前temp的优先级比栈顶运算符的优先级高,则直接进栈
                {
                    operatorStack.push(temp);
                }
                else     // 当不满足如上3种条件时,将操作符栈的栈顶元素push进缓存栈cacheStack之后出栈,重复操作直到操作符栈满足上述3种情况之一为止
                {
                    cacheStack.push(operatorStack.top());
                    operatorStack.pop();
                    continue;
                }
            }
    
            else if (temp == "(")     // 若该队首元素为"(",直接进栈
            {
                operatorStack.push(temp);
            }
    
            else if (temp == ")")     // 若该队首元素为")",弹出操作符栈栈顶元素直到遇见"(",将弹出元素push进缓存栈
            {
                while (!operatorStack.empty())
                {
                    if (operatorStack.top() == "(")
                    {
                        operatorStack.pop();
                        break;
                    }
                    cacheStack.push(operatorStack.top());
                    operatorStack.pop();
                }
            }
    
            q.pop();     // 弹出队首元素,继续下一元素的判断
        }
        while (!operatorStack.empty())     // 将剩下的操作符压入缓存栈cacheStack
        {
            cacheStack.push(operatorStack.top());
            operatorStack.pop();
        }    
    }
    

    calculatingExpression函数是计算部分,同样地,只要理解了计算思路:从左至右扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算(次顶元素 op 栈顶元素),并将结果入栈;重复上述过程直到表达式最右端,最后运算得出的值即为表达式的结果。 一步一步完成即可。

    其中需要注意的一点是,计算时string->int,计算后int->string,应用基于的类型转换。


    void Calculation::calculatingExpression(queue<string> q, bool is_Exceed10)
    {
        if (is_Exceed10)     // 若超过10位数的数字存在为真
        {
            cout << "Input error !  Not exceeding 10 digits expected! " << endl;
            return ;
        }
    
        setPriorityLevel();     // 设置符号的优先级
    	toPostfixExpression(q);     // 将中缀表达式转换为后缀表达式
    
        /**********计算部分***********/
        string postfixExp[SIZE];     // 由于计算时需对缓存栈从栈底到栈顶的逐一扫描,故用string数组进行遍历操作
        int expLen = 0;
        while (!cacheStack.empty())
        {
            postfixExp[expLen++] = cacheStack.top();
            cacheStack.pop();
        }
    
        stringstream stream;     // 用stringstream流进行string和int的格式转换
        for (int i = expLen - 1; i >= 0; i--)
        {
            if (isDigit(postfixExp[i]))     // 若该元素为数字,则直接入数字栈digitStack
            {
                digitStack.push(postfixExp[i]);
            }
            else if (isOperator(postfixExp[i]))     // 若该元素为运算符,则弹出数字栈的两个数进行相应的运算并将结果push进数字栈
            {
                int rightNum = 0;     // 右操作数
                if (!digitStack.empty())
                {
                    stream << digitStack.top();     // 格式转换:string->int
                    stream >> rightNum;
                    digitStack.pop();
                    stream.clear();     // stringstream流的清空,以便重复利用
                }
                int leftNum = 0;     // 左操作数
                if (!digitStack.empty())
                {
                    stream << digitStack.top();     // 格式转换:string->int
                    stream >> leftNum;
                    digitStack.pop();
                    stream.clear();
                }
    
                string res;     // res用于临时存储运算结果,push进数字栈
                if (postfixExp[i] == "+")     // 加法运算
                {
                    stream << (leftNum + rightNum);     // 格式转换:int->string
                    stream >> res;
                    stream.clear();
                    digitStack.push(res);     // 运算结果进栈
                    res = "";
                }
                else if (postfixExp[i] == "-")     // 减法运算
                {
                    stream << (leftNum - rightNum);     // 格式转换:int->string
                    stream >> res;
                    stream.clear();
                    digitStack.push(res);     // 运算结果进栈
                    res = "";
                }
                else if (postfixExp[i] == "*")     // 乘法运算
                {
                    stream << (leftNum * rightNum);     // 格式转换:int->string
                    stream >> res;
                    stream.clear();
                    digitStack.push(res);     // 运算结果进栈
                    res = "";
                }
                else if (postfixExp[i] == "/")     // 除法运算
                {
                    stream << (leftNum / rightNum);     // 格式转换:int->string
                    stream >> res;
                    stream.clear();
                    digitStack.push(res);     // 运算结果进栈
                    res = "";
                }
            }
        }
    
        cout << digitStack.top() << endl;     // 此时,数字栈栈顶保存着最后一次运算的结果,即为表达式的值
    	digitStack.pop();
    }
    

    Part 3

    轮到main()函数登场的时候了,没有挂在Part 1的原因,是想着把Calculation类挂完在写出main函数:


    int main(int argc, char *argv[])
    {
        string input;
        if (!strcmp(argv[1], "-a"))     // 对传入的参数为"-a"的处理
        {
            input = argv[2];
        }
        else
        {
            input = argv[1];
        }
    
        Scan *sc = new Scan();
        Calculation *ca = new Calculation();
    
        // 调用Scan类的ToStringQueue得到string队列
        queue<string> qu = sc->ToStringQueue(input);
        // 若传入参数"-a",则将表达式输出
        if (strcmp(argv[1], "-a") == 0)
        {
            cout << input << "= ";
        }
        // 调用Calculation类的calculatingExpression得到表达式参数的运算结果
        ca->calculatingExpression(qu, sc->getIsExceed10());
    
        // 对象销毁
        delete sc;
        sc = NULL;
        delete ca;
        ca = NULL;
    //    system("pause");
        return 0;
    }
    

    Part 4

    在之前自己的代码上继续开发这种事情还是很因缺思厅的,这次的开发中,如Print类已然成为测试输出用的类,如修改Scan类使其返回队列中区分负数和'-'运算符,如修改注释...等等等等,不过最令人亦可赛艇的还是,纠结着要给自己的程序加上版本1.1呢,还是1.1.0呢(hah~)...

    说的杂乱无章,指点多多的来~

    参考资料:

    The End

  • 相关阅读:
    开发应用资料大全
    shutil库文件的操作
    搭建appium自动化测试环境
    python实现语音录入识别
    go解析markdown转成html
    go指定分隔符格式化时间
    go语言中使用正则表达式
    django_websocket实现简单聊天室
    PyQt5显示日期选择框,获取日期保存文件
    python解决迅雷下载限制的方法
  • 原文地址:https://www.cnblogs.com/monsterJang/p/5373802.html
Copyright © 2020-2023  润新知