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

原帖地址:https://www.cnblogs.com/tangqs/archive/2011/11/03/2234715.html

本文主要内容为123发表于 2011-5-31 00:19:55发表的“逆波兰表达式,北大未名湖站 – 编程技术 – 必度!”帖子

http://www2.pekdo.com/forum.php?mod=viewthread&tid=32&page=1

但帖子中的内容很多不够完善,比如左右单目符号、函数名、函数参数、逗号等都没有给出处理方式,

我做较大的改进,用Delphi 7做了测试,并公开源代码供大家参考,相互学习!

程序下载地址ExpCalculator.rar

源代码下载地址ExpCalcV2.3.rar 

   众所周知,计算机处理表达式的难点在于括号的处理,在通常的表达式中,二元运算符总是置于与之相关的两个运算对象之间,所以,这种表示法也称为中缀表示。波兰逻辑学家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、遇到函数,弹出计算中间值存储栈的相关数据调用函数进行计算;

这样,返回结果就是栈中唯一的数据,我们完成了逆波兰表达式的全部计算过程。

以下为以上算法的详细演示示例:

基于逆波兰式法的数学表达式计算算法举例演示

http://www.cnblogs.com/tangqs/archive/2012/05/18/2507708.html

 最后还有一点就是检查给定表达式是否正确,就是下面的 

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:*/

  /* AddSignParam([‘a’,’s’], [1, 0.5]);  或者 AddSignParam(‘a=1,s=0.5’)  */

  /* CalcExp(‘1+a+sin(s)’, res, infoStr)  */

  /* 其中res存储计算结果,为double型;infoStr存储计算时的提示信息,为string */

表达式计算器 V2.3 支持以下功能:

 1、四则运算 + – * / 、括弧()、正负(+ -)

 2、百分数 %、求幂 ^ 、整数阶乘 ! (1 至 150)

 3、参数符号计算,示例:a+b @@a=1,b=2 结算结果为3

                  用@@表示表达式中定义符号的值

 4、常数e、圆周率PI

 5、丰富的函数功能:

    统计函数:    max,min,sum,avg,stddev 标准偏差,均支持多参数

    三角函数:     sin,cos,tan,arcsin,arccos,arctan

                     degrad(60)   角度转弧度

                     raddeg(3.14) 弧度转角度

                     costh(a,b,c) 余弦定理 cosC)

    指数对数函数:sqrt,power(x,y),abs,exp,log2,log10,logN(a,N),ln

    数据处理函数:int(x),trunc(x) 取整

                        frac(x) 取小数部分

                        round(x) 四舍五入取整

                        roundto(x,-1) 保留一位小数

                        mod(M,N) 求模

    几何面积函数:s_tria(a,b,c) 三角形面积

                        s_circ(r)     圆形面积

                        s_elli(a,b)   椭圆面积

                        s_rect(a,b)   矩形面积

                        s_poly(a,n)   正多边形面积

    平面几何函数:pdisplanes(x1,y1,x2,y2) 平面两点距离

                        pdisspace(x1,y1,z1,x2,y2,z2) 空间两点

                        p_line(x0,y0, A, B, C) 平面点到线距离

                        p_planes(x0,y0,z0 A, B, C, D)空间点到面距离

       数列求和:    sn(a1, d, n) 等差数列前n项和

                        sqn(a1, q, n) 等比数列前n项和

       个税计算函数:intax(x), arcintax(x) 个税反算

    6 、历史计算记录,双击计算记录可重新修改计算

   示例: sin(1)+(-2+(3-4))*20% , e^63+PI , 15! , log2(max(2,3))

   注: 运算符必须为半角格式,三角函为弧度,输入可用空格间隔

Delphi XE移动开发,将文件打包到程序中

用Delphi XE做移动开发的时候,有些情况下可能需要我们把一些文件打包到安装程序中,如网页、图片、数据库文件等,那么如何实现呢

1.首先,打开菜单 Project – Deployment

2.点击添加按钮,选择要添加的文件(文件最好放在工程目录中,这样,即使该工程在其他电脑上打开,也能找到该文件)

3.修改Remote Path:

    当开发Android程序时,Remote Path修改为 assets\internal\ 

    当开发iOS程序时,Remote Path修改为 StartUp\Documents

4.程序中使用文件的方法:

    首先,需要引用 System.IOUtils 文件,这样才能使用TPath类

    TPath.Combine(TPath.GetDocumentsPath,’a.htm’)

    或

    TPath.GetDocumentsPath + PathDelim + ‘a.htm’

    获取文件的绝对路径,然后就可以使用这个路径对文件进行操作了