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

今天花了大概四个小时时间,用栈(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;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值