第四次作业---计算器的第二步
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之后再试了几个结果都没什么错,就是后面碰到的一个,也就是前文所提的红色部分,那里被卡住了,但总的来说也是完美(可能还是有哪里是有问题的,看了我代码的人也可以帮我看看,是不是还有哪里有错误,先谢谢啦)。做出来还是很开心的,敲代码的过程或许是十分枯燥无味的,但是你做出点什么的时候,那种喜悦是其他东西给不了你的,也许这也是程序员能够坚持下来的原因吧。(当然我的代码中还是有很多地方可以简化的,比如定义函数来重复操作那些重复的地方)。这次也明白了博客的重要性,因为很多的学习文件都是从他人博客学来的,这让之前一直没用过博客的我渐渐爱上博客了。。