本文记录了简单计算器的实现思路、易错点以及cpp的代码实现
题目描述
输入输出描述:实现一个没有括号的计算器,输入中没有空格输出计算结果。
实现思路
总体的实现思路是借助栈,当遇见数字时压入数字栈,遇见符号时候压入符号栈之前进行比较,比栈顶高级就压入,比栈顶低级就弹出栈顶符号以及两个数字用于计算,将计算结果重新入数字栈。
原理其实借助了逆波兰表达式,在这里解释一下为什么要进行符号比较。假设栈中有一个+,此时来了一个*,但是*不知道后面是否有比自己优先级更高的,所以先压入栈等待。若又来了一个-,那么此时*就是最高级了,他就可以运算了,并且-还不能入栈,因为+和它是同级,但是+先来,所以按顺序来说需要先算+,所以-需要一直比较到有一个比他级别低或者栈空的情况才能入栈。
在实现中有一个很聪明的思路,就是借助两个永不会出现在表达式中的符号来协助实现。#用于表示栈底,是优先级最低的符号;$用来加在表达式字符串之后,用于表示表达式已结束,优先级次低。这两个符号的联合使用可以简化操作并且同一逻辑。
#优先级最低,所以面对任何符号都可以直接入栈而不用考虑是否栈空。$优先级仅高于#,所以在$入栈时表示栈中没有比他更高级的符号,也就意味着表达式中所有符号已经扫描完毕,也就是计算结果已经可以读取了。
最后说一下如何获取数字的操作。因为是字符串,所以数字每位都是字符,所以要获取两位及以上的数字,需要定义一个函数。注意为了把string变成数字要-’0‘.
int Getnumber(string str,int&i){
int num=0;
while(isdigit(str[i])){
num=num*10+str[i]-'0';
i++;
}
return num;
}
易错点
除了上面的-’0‘,在实现中,最容易错的是double会定义成int,这样会导致小数四舍五入结果出错。所以在涉及到计算的部分已经数据储存的栈或者是临时变量,都要是double类型。没注意这些细节,所以这些问题耗费了很多时间。
还有在计算表达式时,注意参数x与y的相对位置,x在y上面,意味着其比y后出现,按从左到右的计算顺序应该是y+-*/x。
代码实现
#include <iostream>
#include<stack>
#include<string>
using namespace std;
int Getnumber(string str,int&i){
int num=0;
while(isdigit(str[i])){
num=num*10+str[i]-'0';
i++;
}
return num;
}
int Compare(char op){
if(op=='#'){
return 0;
}
if(op=='$'){
return 1;
}
if(op=='+'||op=='-'){
return 2;
}
else{
return 3;
}
}
double Caculate(double x,double y,char op){//注意顺序,x栈位置在y前,表示其出现在y之后
if(op=='+'){
return x+y;
}
if(op=='*'){
return x*y;
}
if(op=='-'){
return y-x;
}
else{
return y/x;
}
}
int main() {
string str;
stack<double>data;
stack<char>oper;
while(getline(cin,str)){
int i=0;
str+="$";
oper.push('#');
while(i<str.size()){
if(isdigit(str[i])){
data.push(Getnumber(str,i));
}
else{
if(Compare(str[i])>Compare(oper.top())){
oper.push(str[i]);
i++;
}
else{
char o=oper.top();
oper.pop();
double x=data.top();//一定是double!!!
data.pop();
double y=data.top();
data.pop();
data.push(Caculate(x,y,o));
}
}
}
cout<<data.top()<<endl;
}
}
结果示意: