您的位置:首页 > 编程语言 > C语言/C++

C++实现任意表达式求值(栈)

2010-02-16 10:51 295 查看
今天花了大概四个小时时间,用栈(stack)实现了“任意表达式的值计算”的问题。

C++ 比 C 好的一点就是,C++ 的STL定义了大量的数据类型和算法,相比于 C 更加视觉化。

实现这个的基本思路很简单:分成两部分完成。两个主要函数:
string shorten(string m) 把 string m 由中缀式变为右缀式,double calculate(string s) 计算右缀式的表达式值。
数字写入右缀式m很简单,不过别忘了用个符号把数字分开。我选择的是$。
比较难的地方是shorten函数,分析怎样把 string m 变为右缀式。可能的计算有这么几种:+,-,*,/,()。于是我通过列表的方式来分析:
+|- *|/ ( 空
+|- m+=c m+=c push push
*|/ push m+=c push push
( push push push push
) m+=c m+=c m+=c XXXX (XXXX表示不可能发生的情况)
竖行是操作符,横行是对应每一个竖行可能遇到的情况,“空”代表栈为空。push表示入栈,m+=c表示元素出栈,写入表达式(右缀式)m。
其实列表的原则挺简单的:
1. 把“(”看做开辟一次新的栈(从“(”开始算,忽视之前的元素,直到“)”被取消);
2. 元素a遇到相同或更高优先级的,栈顶元素c出栈,给右缀表达式m;再次对比;直到遇到更高优先级的,元素a入栈;
3. “)”出现的时候,之前所有元素依次出栈,写入m,直到“(”;
4. 在数字都加入右缀表达式之后,栈中所有元素依次出栈,写入m,直到栈为空。
关键就是 2、3条,我花了好久才总结出来的规律。顺便说下,列表法真的是很好的分析问题的方式,不重不漏还简练。

然后进行右缀表达式的计算,这个比较简单:数字依次入栈,遇到操作符后对栈顶的两个元素进行顺序操作(倒数第二个 +-*/ 倒数第一个),然后把这两个元素换成新的结果。如此循环即可。

原题目要求用int即可,但用int必然会造成除法的不精确。所以考虑用double类型,并且在读取的时候可以支持小数点。记得是有直接转化的函数,但忘了,所以自己写啦,命名为 str2double(string s)。有个dec变量决定计算整数部分还是小数部分,详细可参见函数。

这次实践这个程序还有个小收获,是在我无数次的debug之中:可以在某些关键步骤(比较容易发现bug的地方)加上输出语句,这样可以判断程序是否运行过了这些关键地方。
还有,顺便提一下,“模块化”的思想真的很重要,不然在进行一些小改动时,那种“牵一发而动全身”的感觉很焦虑、很凌乱。
#include <iostream>
#include <string>
#include <stack>
#include <fstream>
using namespace std;

bool isone(char c){
return (c=='+' || c=='-');
}
bool istwo(char c){
return (c=='*' || c=='/');
}

string shorten(string m){
stack<char> s;
string sur;
int i;
char w;
sur;

for(i=0;i<m.size();i++){
if(isdigit(m[i]) || m[i]=='.'){
while(isdigit(m[i]) || m[i]=='.')	sur += m[i++];
i--;
sur += '$';
}
else if(isone(m[i])){
while(s.size() && (isone(s.top()) || istwo(s.top()))){
sur+=s.top();
s.pop();
}
s.push(m[i]);
}
else if(m[i]==')'){
while(s.top()!='('){
sur+=s.top();
s.pop();
}
s.pop();
}
else if(istwo(m[i])){
while(s.size() &&  istwo(s.top())){
sur+=s.top();
s.pop();
}
s.push(m[i]);
}
else s.push(m[i]);
}
while(s.size()){
sur+=s.top();
s.pop();
}
return sur;
}

double tentimes(int n){
double res=1;
for(int i=0;i<n;i++){
res *= 10;
}
return res;
}

double str2double(string s){
double res=0;
char c;
int dec=0;
for(int i=1;i<=s.size();i++){
c=s[i-1];
if(c=='.') dec=i;
else if(!dec) res = res*10 + c-'0';
else res += (c-'0')/tentimes(i-dec);
}
return res;
}

double calculate(string s){
double res, t;
stack<double> num;
string temp;
int i;
for(i=0;i<s.size();i++){
temp="";
if(isdigit(s[i]) || s[i]=='.'){
while(isdigit(s[i]) || s[i]=='.') temp+=s[i++];	//如果最后一位是数字,这样做会出错
num.push(str2double(temp));
}
else{
switch (s[i]){
case '+': t=num.top(); num.pop(); t+=num.top();num.pop();num.push(t);break;
case '-': t=num.top(); num.pop(); t=num.top()-t;num.pop();num.push(t);break;
case '*': t=num.top(); num.pop(); t*=num.top();num.pop();num.push(t);break;
case '/': t=num.top(); num.pop(); t=num.top()/t;num.pop();num.push(t);break;
default: cerr << "Fatal Error! Result would be wrong!" << endl; system("pause");break;
}
}
}
res=num.top();
return res;
}

int main(){
string mid, sur;

cin >> mid;
sur = shorten(mid);
cout << "successfully executed! The right hand operator expression is: " << endl;
cout << sur << endl;
cout << "The result is: " <<calculate(sur) << endl;

system("pause");
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: