四则运算表达式求值

四则运算表达式求值

本文将讨论什么是运算表达式,如何用代码实现中缀表达式转后缀表达式、前缀表达式,以及对后缀表达式的求值。

关于算术表达式

通常我们所看到的算术表达式,运算符总是在两个操作数中间,这样的表达式叫做中缀表达式。与之对应的还有前缀表达式,后缀表达式。中缀表达式易于人的理解,但是使用时往往涉及到运算符的优先级问题,并且需要对括号进行处理。编译系统中对中缀形式的算术表达式的处理方式是: 先把中缀表达式转换成后缀表达式,再进行计算。

表达式的转换和求值方法简述

前缀表达式求值方法
从右往左扫描表达式,遇到数字则压栈,遇到操作符则从栈顶取出两个数进行计算,并将结果压回栈中。

后缀表达式求值方法
方法与前缀表达式求值方法相似,但扫描方向变成从左往右。

中缀表达式求值方法
中缀表达式需要转换成前序表达式或后续表达式。

中缀式转后缀式方法
维护两个栈,运算符栈s1 和 结果栈s2。从左到右扫描表达式。遇到数字直接放s2;遇到操作符时,若:s1为空、自身或s1顶部为’(’、优先级比s1顶部高,则将操作数压入s1,否则将s1顶元素转移到s2,然后继续与最新的s1顶部对比; 若当遇到括号 ‘)’ 时,将s1中第一个’('之前的元素转移到s2中,并丢弃剩余的一对括号。遍历完毕后若s1有剩余则将其转移到s2,s2的逆序弹出结果为对应的后缀表达式。

中缀式转前缀式方法
过程可以参考中缀转后缀的过程,其中差别:
扫描方向是从右往左;
‘(’ 和 ‘)’ 的处理方式要倒转;
与s1顶部对比时,优先级更高或相等都压到s1顶部(之前不包括相等);
最终结果栈的弹出结果不用转逆序;

参考代码:

以下的代码使用C++实现上面提及到的转换和求值过程,支持包括浮点数的四则运算。


#define STRINGS vector	//字符串数组类型别名

//对中缀表达式进行词法分析,分开每个元素放到数组里面,元素包括数字和运算符两大类
//input example: 1+2*3  10.5+((21+34)*4.5)-56.6
STRINGS splitFormula(string formular){
	STRINGS result;
	string buf;
	regex reg("\\d+(\\.\\d+)?|[\\+\\-\\*\\/\\(\\)]");
	smatch matchRes;
	while (regex_search(formular, matchRes, reg)){
		result.push_back(matchRes.str());
		formular = matchRes.suffix().str();
	}
	return result;
}

//枚举表达式中每项元素的类型
enum eleType{IntType, FloatType, OpeType, Unknow};

//判断表达式中元素的类型
eleType judgeType(string ele){
	regex numReg("^\\d+$");
	if (regex_match(ele, numReg)){
		return IntType;
	}
	regex numReg1("^\\d+(\\.\\d+)?$");
	if (regex_match(ele, numReg1)){
		return FloatType;
	}
	regex opeReg("^[\\+\\-\\*\\/\\(\\)]$");
	if (regex_match(ele, opeReg)){
		return OpeType;
	}
	return Unknow;
}

//判断操作符的优先级
int getPriority(string ope){
	if (ope == "+" || ope == "-"){
		return 1;
	}
	if (ope == "*" || ope == "/"){
		return 2;
	}
	return 4;
}

//中缀表达式转后缀表达式
STRINGS inFixToSurFix(STRINGS seq){
	stack<string> tmpRes, tmpOpe;
	for (auto s : seq){
		eleType t = judgeType(s);
		switch (t){
		case IntType:
		case FloatType:
			tmpRes.push(s);
			break;
		case OpeType:
			if (tmpOpe.empty() || s == "("){
				tmpOpe.push(s);
				break;
			} 
			if (s == ")"){
				while (!tmpOpe.empty() && tmpOpe.top() != "("){
					tmpRes.push(tmpOpe.top());
					tmpOpe.pop();
				}
				if (tmpOpe.empty()){
					throw invalid_argument("invalid argument !");
				}
				tmpOpe.pop();	//丢弃'('
				break;
			}
			//对比优先级
			while (!tmpOpe.empty() && tmpOpe.top() != "(" && getPriority(tmpOpe.top()) >= getPriority(s)){
				tmpRes.push(tmpOpe.top());
				tmpOpe.pop();
			}
			tmpOpe.push(s);
			break;

		default:
			throw invalid_argument("Unknow element "+s);
		}
	}
	//处理结果到字符串数组
	while (!tmpOpe.empty()){
		tmpRes.push(tmpOpe.top());
		tmpOpe.pop();
	}
	STRINGS result;
	while (!tmpRes.empty()){
		result.push_back(tmpRes.top());
		tmpRes.pop();
	}
	reverse(result.begin(), result.end());
	return result;
}

//中缀表达式转前缀表达式
STRINGS inFixToPreFix(STRINGS seq){
	stack<string> tmpRes, tmpOpe;
	reverse(seq.begin(), seq.end());	//区别1:从右往左扫描
	for (auto s : seq){
		eleType t = judgeType(s);
		switch (t){
		case IntType:
		case FloatType:
			tmpRes.push(s);
			break;
		case OpeType:
			if (tmpOpe.empty() || s == ")"){	//区别2:括号对待方式倒转
				tmpOpe.push(s);
				break;
			}
			if (s == "("){
				while (!tmpOpe.empty() && tmpOpe.top() != ")"){
					tmpRes.push(tmpOpe.top());
					tmpOpe.pop();
				}
				if (tmpOpe.empty()){
					throw invalid_argument("invalid argument !");
				}
				tmpOpe.pop();
				break;
			}
			//区别3:把tmpOpe顶部优先级比当前更高的都放到tmpRes (不包括相等)
			while (!tmpOpe.empty() && tmpOpe.top()!=")" && getPriority(tmpOpe.top()) > getPriority(s)){
				tmpRes.push(tmpOpe.top());
				tmpOpe.pop();
			}
			tmpOpe.push(s);
			break;
		default:
			throw invalid_argument("Unknow element " + s);
		}
	}
	while (!tmpOpe.empty()){
		tmpRes.push(tmpOpe.top());
		tmpOpe.pop();
	}
	STRINGS result;
	while (!tmpRes.empty()){
		result.push_back(tmpRes.top());
		tmpRes.pop();
	}
	return result;	//区别4:栈输出的结果不用逆序处理
}

//后缀表达式求值
double caculate(STRINGS formula)   {
	stack<string> numStk;
	for (auto e : formula){
		eleType t = judgeType(e);
		double tmpRes = 0.0;
		switch (t){
		case FloatType:	//数字类型直接压进数字栈
		case IntType:
			numStk.push(e);
			break;
		case OpeType:	//遇到操作符,从数字栈中取出两个操作数进行运算,结果返回数字栈
			if (numStk.size() >= 2){
				double n1 = stof(numStk.top());
				numStk.pop();
				double n2 = stof(numStk.top());
				numStk.pop();
				if (e == "+"){
					tmpRes = n2 + n1;
				}
				else if (e == "-"){
					tmpRes = n2 - n1;
				}
				else if (e == "*"){
					tmpRes = n2 * n1;
				}
				else if (e == "/"){
					if (n1 == 0){
						throw invalid_argument("devide 0 error");
					}
					tmpRes = n2 / n1;
				}else{
					throw invalid_argument("unknow operator: " + e);
				}
				numStk.push(to_string(tmpRes));
			}
			else{
				throw invalid_argument("Not enough number from number stack");
			}
			break;
		default:
			throw invalid_argument("Nuknow element from formula: " + e);
		}
	}
	if (!numStk.size() > 1){
		throw invalid_argument("not all number have been used");
	}
	return stof(numStk.top());
}

//打印表达式
void printSeq(STRINGS seq){
	for_each(seq.begin(), seq.end(), [](string s){
		cout << s << " ";
	});
	cout << endl;
	return;
}

int main(){
	try{
		STRINGS seq = splitFormula("56.6+((21.0+34.0)*4.5)/(34.0+21.0)-56.6");
		printSeq(seq);
		STRINGS sufixSeq = inFixToSurFix(seq);	//中缀转后缀
		printSeq(sufixSeq);
		STRINGS prefixSeq = inFixToPreFix(seq);	//中缀转前缀
		printSeq(prefixSeq);
		double result = caculate(sufixSeq);		//后缀求值
		cout << result << endl;
	}
	catch (exception err){
		cout << err.what() << endl;
	}
	return 0;
}

代码运行输出结果

56.6 + ( ( 21.0 + 34.0 ) * 4.5 ) / ( 34.0 + 21.0 ) - 56.6
56.6 21.0 34.0 + 4.5 * 34.0 21.0 + / + 56.6 -
- + 56.6 / * + 21.0 34.0 4.5 + 34.0 21.0 56.6
4.5

你可能感兴趣的:(C++,数据结构与算法)