《面向对象程序设计》 四 计算

第四次作业,还没开始就要deadline了,我的内心嘿嘿嘿嘿嘿。

第一步尝试 计算 优先级的实现

计算器的计算部分最难实现的是优先级的计算。之前完成计算器时查阅过有关资料,了解到需要栈的使用以及前缀后缀中缀表达式的转换。
最开始时我计划将中缀表达式转换为前缀表达式,再对前缀表达式进行求值。着手时发现转换过程需要从右至左扫描中缀表达式(即逆序),而之前的作业中将表达式拆分时运用queue,对于这种先进先出的结构,无法通过两个队列间的转换完成逆序,所以我选择将中缀表达式转换为后缀表达式再求值。

step one: 将中缀表达式转换为后缀表达式

  1. 初始化两个栈,运算符栈signS,储存中间结果的栈numS。
  2. 从左至右扫描中缀表达式。(从队首开始扫描)
    2-1. 遇到操作数,将其压入numS。
    2-2. 遇到括号
    2-2-1. 若为"(",直接压入signS。
    2-2-2. 为")",依次弹出signS栈顶运算符并压入numS,直到遇到"(",将这对括号舍弃。
    2-3. 遇到运算符,比较其与signS栈顶运算符的优先级。
    2-3-1. signS为空或signS栈顶为"("或该运算符的优先级比signS栈顶元素高,该运算符压入signS。
    2-3-2. 其余情况,将signS栈顶元素弹出并压入numS,回到2-2继续比较。
  3. 将signS中元素依次弹出并压入numS。
  4. 此时numS中为后缀表达式的逆序情况.将numS中元素依次弹出并压入signS,signS中即为后缀表达式的正确打开方式。(依赖于栈的操作出栈入栈都对栈顶元素操作完成此逆序过程)。

step two: 后缀表达式求值

  • 1 从左至右扫描signS,将数字压入numS。
  • 2 遇到运算符时,弹出numS栈顶的两个数,用运算符对他们做相应的计算,将运算结果压入numS。
  • 3 最后numS中的一个元素即位所求的值。

代码贴

#include"Calculation.h"
#include<sstream>
#define SIGNNUM 6
#include<iostream>
#include<ctype.h>
using namespace std;
string priority[SIGNNUM][2]={{"(","5"},{")","5"},{"+","1"},{"-","1"},{"*","2"},{"/","2"}}; // 储存运算符优先级数组 

/*返回运算符在优先级数组中的位置,方便优先级比较*/
int Calculation::comPri(string input)
{
    for(int j = 0; j < SIGNNUM; j++)
    {
        if(input == priority[j][0])
        {
            return j; 
        } 
    }
}

/*将中缀表达式转换为后缀表达式*/ 
void Calculation::toPrefix(queue<string> q)
{

    string last;
    int size = q.size();
    numS.push("0"); 
    for(int i = 0;i < size; i++)
    {
        if( isdigit(q.front()[0])  || (q.front()[0] == '-' && isdigit(q.front()[1])) )              
        {
    	
            numS.push(q.front()); 
        }
        else if(q.front() == "(" )
        {
            signS.push(q.front());
        }
        else if(q.front() == ")" )
        {
            while(signS.top()!="(")
            {
                numS.push(signS.top());
                signS.pop();            
            }
            signS.pop();
        }
        else if(q.front() == "+" || q.front() == "-" || q.front() == "*" || q.front() == "/")
        { 

                while(1)
    	    {
                    if(signS.empty() || signS.top() == "(" || priority[comPri(q.front())][1] > priority[comPri(signS.top())][1])
                    {
                        signS.push(q.front()); 
                        break;
                    }
                    else
                    {
                        numS.push(signS.top());
                        signS.pop();
                    }
               }   
        }

        last = q.front();
        q.pop();
    }

    while(!signS.empty())
    {
        numS.push(signS.top());
        signS.pop();
    }
    while(!numS.empty())
    {
        signS.push(numS.top());
        numS.pop();
    }

}


/*后缀表达式求值*/ 
void Calculation::Calculate()
{
    stringstream stream;
    double num1;
    double num2;
    double res;
    string result;
    int size = signS.size();
    for(int i = 0; i < size; i++)
    {
        if(isdigit(signS.top()[0]) || (signS.top()[0] == '-' && isdigit(signS.top()[1])))                   
        {
            numS.push(signS.top()); 
        }
        else if(signS.top() == "+" || signS.top() == "-" || signS.top() == "*" || signS.top() == "/")
        {
            stream << numS.top();
            stream >> num1;
        
            stream.clear();
            numS.pop();
            stream << numS.top();
            stream >> num2;
    
            stream.clear();
            numS.pop();
    
        
            if(signS.top() == "+")
            {
                res = num1 + num2;
            } 
            else if(signS.top() == "-")
            {
                res = num2 - num1;
            }
            else if(signS.top() == "*")
            {
                res = num1 * num2; 
            }
            else if(signS.top() == "/")
            {
                res = num2 / num1;
            }
            stream << res;
            stream >> result;
            stream.clear();
            numS.push(result);
        
        }
    
        signS.pop();
    } 

    stream << numS.top();
    stream >> res;
    cout << res;
    stream.clear();
}

第二部分的完成包括了了解,使用stringstream对象简化类型转换。

样例与注意点:

基本数据类型转换例子 int转string

#include <string>
#include <sstream>
#include <iostream> 
int main()
{
    std::stringstream stream;
    std::string result;
    int i = 1000;
    stream << i; //将int输入流
    stream >> result; //从stream中抽取前面插入的int值
    std::cout << result << std::endl; // print the string "1000"
} 

重复利用stringstream对象

多次转换中使用同一个stringstream对象,记住每次转换前要使用clear()方法。
在多次转换中重复使用同一个stringstream(而不是每次都创建一个新的对象)对象最大的好处在于效率。stringstream对象的构造和析构函数通常是非常耗费CPU时间的。

以上两个部分粗略完成时并没有太大问题,大致思路完成后,一个bug出现了,对"-"的判断。依照之前将表达式拆解存入队列思路,数字与符号显性分开,而"-"可能是减号也可能是负号,目前解决方案为判断"-"的意义,若"-"前并没有符号或"-"前为"(",则判定为符号,先往栈中压入"0"做减法运算得到负数.目前还在调试中,调试是对思维的挑战。(该过程已修正)

修正bug过程,修正题目中超过十位报错的处理,在Calculation类中新添getResult方法,使得计算过程仅需调用getResult一个方法。

    /*计算表达式*/ 
    void Calculation::getResult(queue<string> q)
    {
        Calculation::toPrefix(q);
        Calculation::Calculate();
    }

4.14 修正bug与更新 在付少航姐的脑洞下,考虑如3 * -6 2(3+5)也为合理情况,修改Scan类中拆分表达式的方法,将判断负数过程合并到拆分表达式过程,同时加入对后种情况的判定,补入乘号变为2*(3+5)。
更新一些判断,输入表达式要求()括号匹配,除数要求不为0,否则拒绝计算。
还有很多情况是我没有考虑到或考虑到还没实现的(逃 ,希望一步步能做的更好,有所进步。

第一步参考贴
c++ 字符串流 sstream(常用于格式转换)
前缀后缀中缀表达式


第二步尝试 在命令行中调用编译好的可执行文件

我的理解,c++是面向对象的语言,重要的是对象和对象能调用的方法,而不需要知道方法的具体实现,所以我的主函数只有对对象的创建及调用对象的相应方法。我对自己这部分的理解还存有疑惑,希望老师大神予以解答。
在Scan类中添加judge方法进行判断命令行输入的表达式,将需要进行计算的表达式传给拆分表达式的方法。(已修正,在main函数里处理argc和argv)
同时为了解决传入-a等参数时输出表达式的问题,在Scan类中添加bool类型isPrint,并传给输出表达式的函数。

int main(int argc, char* argv[]) 
{

    string input;	//接收键盘输入的表达式 	
    Scan sc;
    //cin>>input; 

    if(argc == 2)
    {
	    input = argv[1];

    }
    else if(argc > 2)
    {
	    sc.isPrint = true;
	    input = argv [argc-1];
	
    } 

        queue<string> q = sc.toStringQueue(input);  //存放获取的队列 

    Print pr;
    pr.printQue(q);


    Calculation cal;
    cal.getResult(q);

    return 0;
}
原文地址:https://www.cnblogs.com/syaoyao/p/5364145.html