第四次作业---计算器的第二步

1、作业的具体问题网址:

http://www.cnblogs.com/fzuoop/p/5326667.html

2、个人认为问题的难点

  • 创建calculation类,作用是调用scan类传出的queue,进行基本的运算,其中包含了加,减,乘,除,以及括号的运用,难点就在于,你根本不知道要怎么做,如果是简单的加减乘除或许多想几种情况,暴力可以做出,但是加入了括号后很多情况都变了。

  • 如何使用命令行来调用代码,从而做到使用代码。(其中还需要明白如何在main函数中传入参数)

3、针对难点我怎么解决

  • 创建calculation类是这次作业的核心,可以说是百分八十的比重吧,而一开始我根本就不明白如何去做,因为我想的解决方法都被括号这东西硬生生的消灭了,也是我上网去百度了别人的计算器代码,准备看下别人是怎么想的,可是在看完各式各样的代码后的我还是一脸蒙蔽,实在没法子的我去请教同学了,于是她给我推荐了前缀,后缀,中缀,叫我去百度下这个东西,再去慢慢体会,于是我去百度了,看完别人写的有关前缀,中缀,后缀的博客后(后面的参考文件中会贴上网站),我恍然大悟,原来就是这样啊。。。。一切都变的简单了许多,计算器也不再那么神秘,于是很快我就把calculation类敲好了,当然基本的内容是没什么错误了,但是在运行后还是多多少少有编译方面的错误。(其中最坑爹的就是在最后计算的时候调用完成员后没有pop栈顶,导致了死循环,这个浪费了我很多时间)

  • 如何调用命令行,这方面还是有点奇怪,因为我翔跟我说main函数带参数,其实就相当于main是个函数,传入参数,这很好理解,而看了我翔给我的博客(参考文件中会贴出)我也明白了,但是实际运用时就出现问题了,当输入-a时候就炸了,看了半天也没发现错误,于是就用另一种方式,也就是直接cin,而忽略了参数的实际作用,这点我是觉得不好,因为没有实际实现参数的存在意义。

4、贴出代码,scan类和print类在之前的随笔中已经贴过了,这里就不贴了,主要就是贴calculation类和main函数。

calculation类的头文件

#include <iostream>
#include <queue>
#include<stack>
#include<sstream>
#include<string>
#include<string>
using namespace std;

class Calculation
{
	public:
		double  Calculate(queue<string>que);    //用于将输入的queue进行计算 
};

calculation类的cpp文件

#include"Calculation.h"

double  Calculation::Calculate(queue<string>que)
{
	stack<string>operat;                                             //用来存运算符的 
	stack<string>number;                                             //用来存数字的 
	stack<double>num;                                                //用来最后计算的 
	double i,j,k;                                                   //用来在计算时提取stack内的数字,用于计算 
    string temp="",material="";                                     //单纯的用来用来做替代的
	while(!que.empty())                                             //先把传入的队列从中缀改成后缀 
    {
     	temp=que.front();
     	if(temp=="(")
     	{
     	    operat.push(temp);                                                       
     	    que.pop();
     	    if(que.front()=="-")                                   //用来判断是否是负数的负号,而不是减号 ,因为如果在左括号后有负号就一定是负数的负号 
     	    {
     	    	number.push("0");                                   /* 如果是负号的负数的话就可以再数字之前传入一个0,
				                                                      这样把符号当做减号,0减去一个数就相当于其相反值 */ 
     	    	temp=que.front();                                   //这里把负号当做减号 
				operat.push(temp);
				que.pop();                                         //去除掉这个负号 
			}
		}
		else if(temp==")")                                         /*碰到右括号要去符号stack中去从后往前找,直到找到左括号,
		                                                            而这之间的符号就push到数字stack中 */ 
		{
			for(;;)
			{
				if(operat.top()=="(")
				{
					operat.pop();                                  //当然要记得把左括号去掉 
					que.pop();
					break;
				}
				else
				{
					material=operat.top();                        //用来把括号之间的运算符存储入numberstack中 
					number.push(material);
					operat.pop();
				}
			}
		}
		else if(temp=="+" || temp=="-")
		{
			for(;;)
			{
				if(operat.empty() || operat.top()=="(")           //加号和减号只能在运算符stack为空时或者在栈顶为左括号时候导入堆中 
			    {
				    operat.push(temp);
				    que.pop();
				    break;
			    }
			    else
			    {
			    	material=operat.top();
			    	number.push(material);
			    	operat.pop();
				}
			}
		}
		else if(temp=="*" || temp=="/")                
		{
		    for(;;)
			{
			    if(operat.empty() || operat.top()=="(" || operat.top()=="-" || operat.top()=="+")          
			    
				 /*只有当堆顶是除号或者乘号的时候才需要将运算符stack的栈
			     顶转入numberstack中,直到碰到堆顶不是乘号或者除号 */
			     
			    {
			    	operat.push(temp);
				    que.pop();
				    break;
			    }
		      	else
			    {
			        material=operat.top();
			    	number.push(material);
			    	operat.pop();
			    }
		    }
	    }
		else                                                                   //如果不是符号数就是数字了,此时还要和mark相加,因为有可能是负数 
		{
			number.push(temp);
			que.pop();
		}
	 }
	
	
	 while(!operat.empty())                                                    //把最后的一些运算符都存入number中 
	 {
	 	temp=operat.top();
	 	number.push(temp);
	 	operat.pop();
	 }
	
	
	
	 while(!number.empty())                                                    //把number堆中的东西反过来存到operat中,用于计算 
	 {
	 	operat.push(number.top());
		number.pop();
	 }
	
	double d=0;                                                                /*以免出现-(100+9)这种情况的出现 这个很重要,
	                                                                           如果第一个不是-(这种类型的话,也无影响 */
	num.push(d);
	
	while(!operat.empty())                                                     //进行计算 
	{
		temp=operat.top();
		if(temp=="-")
		{
			j=num.top();
			num.pop();
			k=num.top();
			num.pop();
			i=k-j;
			num.push(i);
			operat.pop();
		}
		else if(temp=="+")
		{
			j=num.top();
			num.pop();
			k=num.top();
			num.pop();
			i=j+k;
			num.push(i);
			operat.pop();
		}
		else if(temp=="*")
		{
			j=num.top();
			num.pop();
			k=num.top();
			num.pop();
			i=j*k;
			num.push(i);
			operat.pop();
		}
		else if(temp=="/")
		{
			j=num.top();
			num.pop();
			k=num.top();
			num.pop();
			i=k/j;
			num.push(i);
			operat.pop();
		}
		else
		{
			stringstream stream;
			stream<<temp;
			stream>>i;
			num.push(i);
			operat.pop();
		}
	}
	
	double result=0;                                                          //使result等于计算出的结果并且return出去 
	result=num.top();
	num.pop();
	return result;
}

main 函数

#include <iostream>
#include <stdio.h> 
#include <string.h>
#include<queue>
#include"Scan.h"
#include"Print.h"
#include"Calculation.h"
using namespace std;

int main(int argc,char *argv[])
{
	Scan get;                                                                     //定义scan类 
	Print output;                                                                 //定义print类,但是这里没什么用。。。。 
	Calculation cal;                                                              //定义calculation类,用于计算结果 
	string input,_input;                                                          //定义一个字符串用来存储输入要计算的式子  
	cin>>input;
    if(input=="-a")                                                               //如果有输入-a的话就得输出整个式子 
	{ 
	    cin >>_input;                                                             /*如果不是-a的话既不必在输入,因为input就已经是要计算的式子了,
		                                                                          如果是-a则需要再输入要计算的式子 */ 
	    cout<<_input;                                                             // 输出式子 
		cout<<"= ";                                                               //格式要求
		get.ToStringQueue(_input);                                                
	}
	else
	{
	    get.ToStringQueue(input);                                                /*把输入的input放入Scan类的函数进行计算,把计算后的值赋予
		                                                                           Scan的成员 outputqueue*/
	}                                                  
    if(get.outputqueue.front()=="ERROR")                                         //看是否输入的式子是有问题的,如果有问题就输出error 
    {
    	cout<<"ERROR"<<endl;
	}
	else
	{
		cout<<cal.Calculate(get.outputqueue)<<endl;                              //输出结果 
	}
	return 0;
}

5、测试数据的结果

更新的结果(因为发现之前的-(-(1+1)+2)是算不出的。。。。。没考虑到括号内最前面是符号的情况)

6、这次的收获

  • 明白了 前缀 中缀 后缀 的思路

  • 知道了sstream的使用方法(将字符串类型转化成为其他int,double子类的类型)

  • stack和queue的使用方法(明白区别,queue是先进先出,stack是后进先出)

7、参考文件(看了很多,真正用到的就这些)

(1).命令行,main函数传递参数:
http://www.cnblogs.com/avril/archive/2010/03/22/1691477.html

(2).前缀 后缀 中缀(核心思路):
http://m.blog.csdn.net/article/details?id=6763722

(3).stack和queue的使用:
http://www.cnblogs.com/mfryf/archive/2012/08/09/2629992.html

(4).sstream的使用(将字符串转化为别的类型):
http://blog.163.com/zhuandi_h/blog/static/180270288201291710222975/

8、总结

一路上磕磕碰碰,但也是圆满完成(应该圆满了吧。。。。。),一路上少不了同学的帮助,一起讨论,也是一种乐趣,每一次的成功都会让你觉得之前的努力是值得的,就像我一开始以为一点小错误一直弄的程序无法运行,但是当我修改后,输入1+1这样简单的式子时候,结果是2,我就十分开心,哪怕是简单的等于2,我也知道我基本是成功的,就算后面还有点小问题,但最难的一坑已经迈过去了,而这次我想的也算齐全,从而我的代码在运行出1+1=2之后再试了几个结果都没什么错,就是后面碰到的一个,也就是前文所提的红色部分,那里被卡住了,但总的来说也是完美(可能还是有哪里是有问题的,看了我代码的人也可以帮我看看,是不是还有哪里有错误,先谢谢啦)。做出来还是很开心的,敲代码的过程或许是十分枯燥无味的,但是你做出点什么的时候,那种喜悦是其他东西给不了你的,也许这也是程序员能够坚持下来的原因吧。(当然我的代码中还是有很多地方可以简化的,比如定义函数来重复操作那些重复的地方)。这次也明白了博客的重要性,因为很多的学习文件都是从他人博客学来的,这让之前一直没用过博客的我渐渐爱上博客了。。

原文地址:https://www.cnblogs.com/zxlmhh/p/5379181.html