首页 > 技术文章 > 面向对象程序设计第四次作业(2)

HBING 原文

计算四则运算表达式


0、 运行效果



1、 前期准备

  • 网上各种找资料,各种问同学。其实最重要的还是自己要能理解 (⊙ˍ⊙) 讲真。光是理解别人怎么写我就花了好久的时间 (。_。)

  • 真心要理解透才能做得出来 (⊙ˍ⊙) 网上的方式跟题目要求的都不一样...别人都是转成前缀式或后缀式,把数字和符号存到一起...题目却要求把数字和符号分别存入栈,简直造孽...


2、 问题与解决

  • 先说一句自认为十分重要的话“一口吃不成大胖子”,讲真。刚开始的时候又想弄好对负数的处理又想弄好对优先级的判断,反而哪一件事情都没有做成,还把自己弄的很烦躁。后来就一步一步解决:
    对“-a”和负数处理 -> 无括号运算 -> 有括号运算
  • 一步一步来还有一个好处就是debug的时候,对代码调试的范围就会小很多,debug的效率也会提高。
  • 赤裸裸的血的教训...真的很重要啊...


<1> 重复利用stringstream对象要记得.clear()

  • 刚开始用的时候没有.clear(),发现怎么算都不对 0 0 回头把存符号和存数字的栈分别输出发现都是同一个符号或数字...翻翻资料发现要.clear()


<2> 对负号的处理

  • 最开始想的是判断是否 - 前面是字符&&后面是数字,如果是的话就把数字取负,-换为+再入栈。

  • 后来发现会有(-(1+3)*2+1)这样的情况,虽然规定一下输入格式,改写成(1-(1+3)*2)就可以没有问题了,但是这样计算器感觉用起来就不是那么方便了。

  • 通过学霸指导,我就把对负号的处理改为在-前面补0。原先有想就在Calculation类里面操作的,但是感觉不好判断-号前面是否是负号..然后我就跑去上次写的scan类里面在字符串存入queue的时候直接补0,感觉这样会比较好操作一点。

  • 改完之后的试着输出一下,符合!!


<3> 计算表达式的值

  • 感觉用栈的话适合用前缀式进行计算比较方便,不过不知道我怎么查的...查到的基本上都是用后缀式进行计算的...等我后缀式理解的七七八八了才发现有前缀式 (⊙ˍ⊙)

  • 然后我应该用了不算后缀式的后缀式吧..

  • 正宗后缀式:

  • 我是再开了两个栈分别用来暂时存储符号和数据,大概想法(类似后缀式计算)如下(=o=)

  • 其实+ - * /调试好之后,对括号的处理会方便很多,要考虑的情况也不算多,胜利的曙光就在眼前...


<4> 对栈的调用

  • 其实用法没什么难点,就是一定要注意你在调用.top().pop()的时候stack是否为空!!!!!!!

  • 这点真的相当重要啊!!!无数次挂在这上面...写完一定要认真考虑每一次调用.top().pop()的时候stack是否为空!!!每一次!!!


<5> 心态

  • 在ddl还没延期之前,其实挺烦躁的...一大堆事情等着去做,眼看着ddl马上就要到了这次作业却还是不怎么会做,代码到处都是bug。
  • 在此要感谢浩晖大神对我的...谆谆教诲?好吧,我不知道用什么词来形容 (゚ー゚)
  • ddl截止的倒数第二天晚上吧,那时候感觉基本思路貌似已经成型了,但具体又不知道怎么下手,超级烦躁。去问浩晖大神也还是不是很懂,然后就负气的说了一句算了不看了也来不及了,之后就滚去睡觉了。第二天早上起来发现皓辉大神发了一大段话,大概意思就是 有没有做出题目本身并不重要,题目太难可以降低难度一步步实施,不要轻易放弃
  • 看完之后就满血复活了hhh 而且说来也奇怪,第二天我就都做出来了 (>▽<)
  • 再一次感谢浩晖大神对我的鼓励!!!

3、 待改进

  • 目前写的代码都是默认输入的表达式完全合法,一旦表达式不合法就会有各种问题了 0 0
  • 还有计算结果的处理。没办法像真正计算器那样限定总数字长度,有几位小数就输出几位小数。只能做到限制小数点后几位,如果小数点后没有小数位或不足限定位数时会补0。看起来总觉得是四舍五入后的结果...

4、 代码

  • 自己试了些小数据暂且没有问题。


2016/04/13 更新

  • 今天发现了一个八阿哥 (。_。)

  • 测试数据:-10000+20-(-(-20+2)*3-1/(2+6*3)-8)+2

    • 不得不说那些被注释掉的代码挽救了我一命!!没花很久的时间就发现了bug ︿( ̄︶ ̄)︿
    • 每次移动符号和数字的时候都要判断一下符号是否为)之前测的数据都比较巧合 0 0 刚好左括号后面都不是没有括号的计算..好像也不是这么说...原数据如果改成-10000+20-(-(-20+2)*3-1/(2+6*3)-8),在代码没有修改的情况下是可以运行的 0 0
    • 运行效果
    • 不过小数点后面的竟然被四舍五入了...更:coutprintf就可以了 0 0
    • 以下代码已更新

  • Calculation.h

    #pragma once
    #include <iostream>
    #include <stack>
    #include <string>
    #include <queue>
    using namespace std;
    
    class Calculation 
    {
    
    public:
    
    	//计算表达式的值
    	void ToCalculate(queue<string>q);
    
    private:
    
    	double m_dTemp;
    	char m_cTemp;
    
    	//存储数字
    	stack<double>figure;
    	stack<double>figure_temp;
    
    	//存储符号
    	stack<char>operate;
    	stack<char>operate_temp;
    
    };
    

  • Calculation.cpp

    #include <iostream>
    #include <queue>
    #include <sstream>
    #include <string>
    #include <stack>
    #include "Calculation.h"
    using namespace std;
    
    void Calculation::ToCalculate(queue<string>q)
    {
    	int operate_priority(char c);
    	double calculate(char oper, double front, double behind);
    
    	double _figure;
        stringstream stream;
    	string m_strQue;
    
    	//数字符号分别入stack
    	while (!q.empty()) 
    	{
    		m_strQue = q.front();
    
    		if (m_strQue[0] < '0' || m_strQue[0] > '9')
    		{
    			stream << m_strQue;
    			stream >> m_cTemp;
    
    			operate.push(m_cTemp);
    			stream.clear();
    		}
    		else
    		{
    			stream << m_strQue;
    			stream >> m_dTemp;
    
    			figure.push(m_dTemp);
    			stream.clear();
    		}
    
    		q.pop();
    	}
    
    	/*
    	while (!operate.empty()) {
    		cout << operate.top() << endl;
    		operate.pop();
    	}
    	cout << "**************" << endl;
    	while (!figure.empty()) {
    		cout << figure.top() << endl;
    		figure.pop();
    	}
    	*/
    	
    	while (!operate.empty()) 
    	{
    		//遇到左括号
    		if (operate.top() == ')') 
    		{
    			operate_temp.push(operate.top());
    			operate.pop();
    		}
    		
    		else
    		{
    			if (operate_temp.empty())
    			{
    				operate_temp.push(operate.top());
    				figure_temp.push(figure.top());
    
        			operate.pop();
        			figure.pop();
    
        			//operate可能为空
            		if (operate.empty())
    	        	{
    		        	break;
    		        }
    
        		}
    
        		//运算符优先级比较计算 
            	if ((operate_priority(operate.top()) < operate_priority(operate_temp.top())) && (operate_temp.top() != ')'))
    	        {
        			//除数为0
        			if (operate_temp.top() == '/'&&figure_temp.top() == 0) 
            		{
            			exit(0);
            		}
    
    		        _figure = calculate(operate_temp.top(), figure.top(), figure_temp.top());
    
        			operate_temp.pop();
            		figure.pop();
    	        	figure_temp.pop();
    
        			figure.push(_figure);
            	}
    	        else
    	        {
            		//遇到左括号
            		if (operate.top() == ')')                    
            		{                                             
            			operate_temp.push(operate.top());     
            			operate.pop();                              
            		}                                             
                                                                                   
            		else                                            
            		{                                                
    	        		operate_temp.push(operate.top());            
    	        		figure_temp.push(figure.top());              
                                                                                       
    	        		operate.pop();                               
    	        		figure.pop();                         
    	        	}                                             
            		
    	        }
    
        		//括号匹配
            	if (!operate.empty() && !operate_temp.empty() && (operate.top() == '('&&operate_temp.top() == ')'))
    	        {
    		        operate.pop();
    			        operate_temp.pop();
    			}
        		
            }
    
        	/*
            cout << "operate" << endl;
    		if (!operate.empty()) {
        		cout << operate.top() << " ";
            }
        	cout << endl;
            cout << "operate_temp" << endl;
    		if (!operate_temp.empty()) {
        		cout << operate_temp.top() << " ";
            }
    		cout << endl;
        	cout << "figure" << endl;
            if (!figure.empty()) {
    	        cout << figure.top() << " ";
            }
    		cout << endl;
        	cout << "figure_temp" << endl;
            if (!figure_temp.empty()) {
    	        cout << figure_temp.top() << " ";
            }
    		cout << endl;
        	cout << "****************************" << endl;
            */
        }
    	
        /*
        cout << "operate" << endl;
    	while (!operate.empty()) {
        	cout << operate.top() << " ";
            operate.pop();
        }
        cout << endl;
    	cout << "operate_temp" << endl;
        while (!operate_temp.empty()) {
            cout << operate_temp.top() << " ";
        	operate_temp.pop();
    	}
    	cout << endl;
        cout << "figure" << endl;
    	while (!figure.empty()) {
        	cout << figure.top() << " ";
            figure.pop();
        }
    	cout << endl;
        cout << "figure_temp" << endl;
        while (!figure_temp.empty()) {
            cout << figure_temp.top() << " ";
            figure_temp.pop();
        }
        cout << endl;
        cout << "--------------------------" << endl;
        */
    
        while (!operate_temp.empty()) 
        {
    		//除数为0
    		if (operate_temp.top() == '/'&&figure_temp.top() == 0)
    		{
    			exit(0);
    		}
    
            _figure = calculate(operate_temp.top(), figure.top(), figure_temp.top());
            
            operate_temp.pop();
    		figure.pop();
        	figure_temp.pop();
            
            figure.push(_figure);
        }
        
        cout << figure.top() << endl;
    }
    
    //判断符号优先级
    int operate_priority(char c)
    {
        int pri;
    	switch (c) 
        {
        case '+':
        case '-':
            pri = 0;
    		break;
        case '*':
    	case '/':
        	pri = 1;
            break;
    	case '(':
        	pri = -1;
            break;
    	case ')':
        	pri = 2;
            break;
        }
        return pri;
    }
                
    //计算
    double calculate(char oper, double front, double behind) 
    {
        double result;
    	switch(oper)
        {
    	case '+':
        	result = front + behind;
            break;
    	case '-':
        	result = front - behind;
            break;    
        	case '*':
    		result = front * behind;
        	break;
        case '/':
            result = front / behind;
            break;
    	}
        return result;
    }
    
  • 虽然阅读体验可能不太好,但是那些被注释掉的代码都是我流过的血和泪...实在舍不得删除...


5、 参考资料

推荐阅读