在最近的项目中希望使用一个简单的表达式计算功能, 网上有开源的库,但是体积庞大,编译链接缓慢。
所以最后还是自己实现了一个。 支持几乎所有的数学运算符, 以及<cmath> 内置的函数。
实现原理简述:
c++实现表达式计算的思路一般有两种, (此处使用的是后者。)
1. 使用面向对象的多态特性, 对每一种运算符实现一个表达式类, 类似
class Exp;
class AddExp:Exp;
class MulExp:Exp;
....
虚拟重载evaluate()求值函数, 在解析表达式的过程中构建一颗表达式树,同时根据运算符优先级对树做旋转。
2. 基于堆栈
使用两个栈 O 和 N, 一个存算符, 一个存操作数, 根据算符优先级出入栈。
线性扫描表达式, 比较当前算符 和栈顶算符的优先级(还要考虑结合性),如果后者优先级高, 则从数值栈顶取出元素并计算,然后把返回值入栈。
否则 操作符/操作数依次入栈
实例: 计算 1*2+3 具体过程
依次入栈
push 1; push *; push 2
此时栈内元素如下, 下一个算符为'+'
N |1|2|
O |*|
由于栈顶端算符为*, 优先级高于+, 所以先计算*, 从数值栈顶取出两个操作数1和2, 相乘得返回值2,入栈
pop 1; pop 2; pop *; push 1*2
N |2|
O |
然后再处理后面的‘+’
N |2|3|
O |+|
-->
N |5|
O |
大致思路如上述
3. 实现
所以最后还是自己实现了一个。 支持几乎所有的数学运算符, 以及<cmath> 内置的函数。
实现原理简述:
c++实现表达式计算的思路一般有两种, (此处使用的是后者。)
1. 使用面向对象的多态特性, 对每一种运算符实现一个表达式类, 类似
class Exp;
class AddExp:Exp;
class MulExp:Exp;
....
虚拟重载evaluate()求值函数, 在解析表达式的过程中构建一颗表达式树,同时根据运算符优先级对树做旋转。
2. 基于堆栈
使用两个栈 O 和 N, 一个存算符, 一个存操作数, 根据算符优先级出入栈。
线性扫描表达式, 比较当前算符 和栈顶算符的优先级(还要考虑结合性),如果后者优先级高, 则从数值栈顶取出元素并计算,然后把返回值入栈。
否则 操作符/操作数依次入栈
实例: 计算 1*2+3 具体过程
依次入栈
push 1; push *; push 2
此时栈内元素如下, 下一个算符为'+'
N |1|2|
O |*|
由于栈顶端算符为*, 优先级高于+, 所以先计算*, 从数值栈顶取出两个操作数1和2, 相乘得返回值2,入栈
pop 1; pop 2; pop *; push 1*2
N |2|
O |
然后再处理后面的‘+’
N |2|3|
O |+|
-->
N |5|
O |
大致思路如上述
3. 实现
1. 实现一个函数 tokenize 把表达式拆解为一个一个独立的token 忽略空格等
此处的token指 数值/算符/变量
2. 实现一个函数evaluate 根据上述原理扫描已经过预处理的token序列, 实现计算。
3.对左值的处理也考虑了, 具体见源码。代码见资源。
http://download.csdn.net/detail/tiankong_bear/9893557
更正:
上面的代码里面有一个bug
line 400 开始应该改为
if (RPAR == it->opr) {
if (prevOp != LPAR) {throw ExprError("parenthese unmatch");}
sOpr.pop();
} else {
sOpr.push(it->opr);
}
否则会有括号匹配问题。
资源代码 已更正。