栈的应用——实现简易计算器(一)

时间:2023-01-09 10:45:39

周日晚上心血来潮,想用栈做个简单的计算器,于是便动手鼓捣了。参照书上的思想,我用了两个栈来解决这个问题。
1)栈S1用来存放运算符,栈S2用来存放操作数。由于运算符为char型而操作数为int型,因此,需要使用类模板来声明两个不同数据类型的栈。
2)为简单起见,只能计算0-9之内的数(由于每次只能读入一个字符入栈,如果是大于等于10的任意数,就要设计另外的算法使“相邻”的两个数字组合为一个数)。
3)运算符涉及优先级的问题,为此,需要一个判断优先级的函数Priority(),该函数返回运算符的优先级。现在的问题是,对于最初输入以及最后入栈的运算符,它与谁进行比较?为此,先将一符号“#”压入栈中作为栈底,并且在输入表达式时以“#”号结尾。一开始我在Priority()函数中令“#”的优先级最低,但之后我发现有更为简便的方法:在该函数中只要遇到“#”或“(”或“)”,就返回-1,对于“(”还有另一更简单的处理方式:只要一读入“(”,就让其入栈。从代码中可以知道,这两种处理方式是不冲突的。

这是本人做的第一个“小程序”,虽然到了这一步仍然只能计算10以内的整数的加减乘除,但是这毕竟是多次调试、改错并优化后的结果,在这个过程中也得到了很多经验,对栈的理解也更透彻了。本来想把之前阶段性的代码贴出来,并附上解决方法,但由于没有保存之前的代码,那些碰到的问题以及解决方法都无法分享了sigh。

以下为完整代码:

#include"SeqStack.h" //该头文件的声明在另一篇博客中

int Calculate(SeqStack<char>&);
int Priority(char);

int Priority(char op) //判断输入的运算符的优先级
{
switch(op){
case '+': case '-':return 1;
case '*': case '/':return 2;
default: return -1;
}
}

int Calculate(SeqStack<char>& S1) //S1为char型的栈,用于存放运算符
{
SeqStack<int>S2; //S2为int型的栈,用于存放操作数
char ch,a,b,c,d,m,n,p,t;
int op1,op2,op3,op4,op5,op6,op7,op8,a1,a2,n1,n2;
S1.push('#'); //先将#存入栈S1中
cout<<"请输入需要计算的表达式(表达式需以#结尾):\n";
do{
ch = getchar();
switch(ch){
case '0': case '1':
case '2': case '3':
case '4': case '5':
case '6': case '7':
case '8': case '9':
S2.push(ch - '0'); //将char型的数值转换为int型并压入栈S2中
break;
case ' ': //若输入的表达式含有空格,则转入下一字符
break;
case '+':
if(Priority(S1.get_top()) < Priority('+')) //若当前栈顶运算符优先级低于‘+’,则将当前扫描到的字符‘+’入栈
S1.push('+');
else{
S2.pop(op1);
S2.pop(op2); //将栈S2最外面的两个数字弹出
if(S1.get_top() == '+') //判断S1栈顶的运算符,以做出相应的运算
S2.push(op2 + op1);
else if(S1.get_top() == '-')
S2.push(op2 - op1);
else if(S1.get_top() == '*')
S2.push(op2 * op1);
else
S2.push(op2 / op1);
S1.pop(c); //该运算符已做过相应的运算,出栈
S1.push('+'); //将‘+’压入栈中,作为S1新的栈顶
}
break;
case '-':
if(Priority(S1.get_top()) < Priority('-'))
S1.push('-');
else{
S2.pop(op3);
S2.pop(op4);
if(S1.get_top() == '+')
S2.push(op4 + op3);
else if(S1.get_top() == '-')
S2.push(op4 - op3);
else if(S1.get_top() == '*')
S2.push(op4 * op3);
else
S2.push(op4 / op3);
S1.pop(d);
S1.push('-');
}
break;
case '*':
if(Priority(S1.get_top()) < Priority('*'))
S1.push('*');
else{
S2.pop(op5);
S2.pop(op6);
if(S1.get_top() == '+')
S2.push(op6 + op5);
else if(S1.get_top() == '-')
S2.push(op6 - op5);
else if(S1.get_top() == '*')
S2.push(op6 * op5);
else
S2.push(op6 / op5);
S1.pop(t);
S1.push('*');
}
break;
case '/':
if(Priority(S1.get_top()) < Priority('/'))
S1.push('/');
else{
S2.pop(op7);
S2.pop(op8);
if(S1.get_top() == '+')
S2.push(op8 + op7);
else if(S1.get_top() == '-')
S2.push(op8 - op7);
else if(S1.get_top() == '*')
S2.push(op8 * op7);
else
S2.push(op8 / op7);
S1.pop(m);
S1.push('/');
}
break;
case '(': //只要一遇到左括号,即将其压入栈中
S1.push('(');
break;
case ')':
do{
S2.pop(n1);
S2.pop(n2);
if(S1.get_top() == '+')
S2.push(n2 + n1);
else if(S1.get_top() == '-')
S2.push(n2 - n1);
else if(S1.get_top() == '*')
S2.push(n2 * n1);
else
S2.push(n2 / n1);
S1.pop(n); //将已参加过运算的运算符出栈
}while(S1.get_top() != '('); //通过该循环保证括号中的所有表达式已计算完毕
S1.pop(p); //更重要的是要将左括号退出 右括号从未进栈
break;
case '#': //输入的表达式需以#结尾,方便判断运算符的优先级
S2.pop(a1);
S2.pop(a2);
if(S1.get_top() == '+')
S2.push(a2 + a1);
else if(S1.get_top() == '-')
S2.push(a2 - a1);
else if(S1.get_top() == '*')
S2.push(a2 * a1);
else
S2.push(a2 / a1);
S1.pop(a); //将当前运算符出栈
S1.pop(b); //将栈底的#出栈,至此,栈S1已空。
break;
default:
break;
}
}while(ch != '\n'); //若扫描至换行符,则退出循环,至此,表达式计算完成。
return S2.get_top(); //S2的栈底元素即为最终的结果
}

int main()
{
SeqStack<char>S;
cout<<Calculate(S);
}

标题上加了个(一),希望之后会有(二)甚至更多的文章吧,把这个计算器再优化些,功能再丰富些:)

附一张模拟表达式出栈、入栈过程的手稿:
栈的应用——实现简易计算器(一)