分享Delphi实现数学表达式的计算(逆波兰式法)-四则运算解析解决方法

分享Delphi实现数学表达式的计算(逆波兰式法)-四则运算解析
http://www.cnblogs.com/tangqs/archive/2011/11/03/2234715.html
众所周知,计算机处理表达式的难点在于括号的处理,在通常的表达式中,二元运算符总是置于与之相关的两个运算对象之间,所以,这种表示法也称为中缀表示。波兰逻辑学家J.Lukasiewicz于1929年提出了另一种表示表达式的方法。按此方法,每一运算符都置于其运算对象之后,故称为后缀表示。如a+b表达为ab+。这种后缀表达式非常方便去掉运算符优先级的影响与括号,甚至是单目运算符:
示例说明:
1. a*b+c*(d+e)   逆波兰表达式: ab*cde+*+
2. -a+-b*+c        逆波兰表达式: a~b~c@*+
3.a-(-(b-c))       逆波兰表达式: abc-~ -
预处理:
-(负号)处理:用~代替
+(正号)处理:用@代替,或者将其在字符串中删除
数学自然常数 e 与圆周率 pi 的用  (2.718281828459045)  与 (3.1415926535897932384) 代替(包含前后的括弧)

那么计算机怎样通过后缀式来进行运算呢?这里首先假设读取分析表达式的准备工作都已经做好了,那么首先需要做的是把表达式转换成后缀式,也就是逆波兰表达式的构建过程。
构建器由两个主要组件组成,一个是目标表达式的存储器,另一个是一个符号栈。与源表达式的扫描顺序一样,存储器是从左向右储存数据的,而符号栈则遵守后进先出的原则:
* 读入一个数据(数值与函数名非单个字符,需要做判断处理)
1. 如果是左单目运算符或者函数名,直接入符号栈;比如 正负号 ~ @ max sin
2. 如果是右单目运算符,直接入存储器栈;比如 阶乘!与百分号%
3. 如果是运输量,则直接写入存储器;检查符号栈顶是否有单目运算符,有的话则全部出栈,并写入存储器;
4. 如果是左括号"(",则直接入符号栈;
5. 如果是右括号")",则弹出符号栈数据,写入存储器,一直到左括号弹出,再检查栈顶是否为左单目运算符或者函数名,是的话继续弹出,直到遇到双目运算符;
6. 如果是普通运算符,则与栈顶符号比较优先级,若大于栈顶优先级,则入栈;否则弹出栈顶符号并写入存储器,直到栈顶符号的运算优先级较小为止;
7.如果是函数参数的连接逗号“,”时,则弹出符号栈数据,直到遇到左括弧 ( 或者逗号,为止,再将逗号,入符号栈;
8.如果是结束符(表示表达式已全部读完),则符号栈全部弹出并写入存储器,否则读取数据进入下个过程;
此外还有一些处理的技巧,比如定义一个优先级最低的运算符作为表达式结束的标志(用#标示,添加在表达式结尾处),在符号栈里首先加入一个结束标志,那么表达式读完时则自动弹出栈中所有符号,并写入存储器结尾表示成功。
接下来是计算的过程。计算的时候除了刚才构建的数据外,还需要另外一个计算中间值存储栈。
1、首先是从左至右扫描数据段,如果读出的是数据则压入计算中间值存储栈;
2、遇到单目运算符号就从计算中间值存储栈弹出一个数据进行运算,再把结果压回计算中间值存储栈;
3、遇到双目运算符号就从计算中间值存储栈弹出两个数据进行运算,再把结果压回计算中间值存储栈;这里需要注意减法与除法的计算顺序是第一次弹出的值作为减数和除数,第二次弹出的值作为被减数和被除数。
4、遇到逗号,就从计算中间值存储栈弹出两个数据用“,”连接起来直接将数值字符串压入计算中间值存储栈,不做计算。比如 12 13 , 压入13,12
5、遇到函数,弹出计算中间值存储栈的相关数据调用函数进行计算;
这样,返回结果就是栈中唯一的数据,我们完成了逆波兰表达式的全部计算过程。
 最后还有一点就是检查给点表达式是否正确,就是下面的 
function CheckCalcExp(const ExpT: string; var AInfo: string): boolean;
这样保证计算不会出错,具体见代码。
/*   表达式计算                                                                                                */
  /*   调用方式:CalcExp('1+max(0.5,sin(1))+sum(1,2^3,mod(5,3))', res, infoStr)   */
  /*   带符号参数调用方法,先调用符号定义AddSignParam,再调用 CalcExp:                 */