实验12 表达式求值

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

课前讲解—表达式求值

一、表达式求值的规则

表达式求值是程序设计语言编译中的一个基本问题。它的实现就是对“栈”的典型应用。

首先了解算术四则运算的运算规则:

(1)先乘除,后加减。

(2)从左到右计算

(3)先算括号内,再算括号外

因此,下面这个算数表达式的计算顺序应为:

4+2*3 - 10/5

= 4 + 6 - 10 / 5

= 10 - 10 / 5

= 10 - 2

= 8

任何一个表达式都由操作数(operand)、运算符(operator)和界定符组成:

●操作数即可以是常量,也可以是被说明为变量或常量的标识符。

●运算符可以分为算术运算,关系运算和逻辑运算符。

●界定符有左右括号和结束符等。

为了叙述的简洁,我们仅讨论简单算数表达式的求值问题,这种表达式只包含加、减、乘、除等四种算术运算。需要时,不难把它推广到更一般的表达式上。

二、运算符优先级

对于连个相继出现的操作符θ1和θ2有三种关系:

θ1<θ2θ1的优先级低于θ2

θ1 =θ2θ1的优先级等于θ2

θ1>θ2θ1的优先级高于θ2

由此可以列出“+-*/”之间的优先级。如下表:

加减乘除优先性都低于“(”但是高于“)”,由运算从左到右可知,当θ1=θ2 ,令θ1>θ2

为了算法简洁,在表达式的左边和右边虚设一个“#”,这一对“#”表示一个表达式求值完成。

“(”=“)”当一对括号相遇时表示括号内已运算完成。

“)”和“(”、“#”和“(”、“(”和“#”无法相继出现,如果出现则表达式出现语法错误。(实现时,可以用0存储) 这个表如何理解呢?例如:a+b+c,这里有两个运算符,运算符θ1为+,运算符θ2也为+ ,查上表,得到“>”的关系,那么意味着先计算前面的+号,也就是先算a+b,得到结果后,再考虑算后面的表达式。

三、算法思路

为实现优先算法,可以使用两个工作栈,一个是OPTR,用于寄存运算符,一个是OPND,用于寄存操作数和运算结果。算法的基本思想是:

1. 首先置操作数栈为空栈,表达式起始符“#”为栈底元素。

2. 依次读入表达式中的每个字符,若是操作数则进OPND栈,若是运算符则和OPTR栈的栈顶运算符比较优先级作相应操作,直至整个表达式求值完毕(OPTR栈顶元素和当前读入的字符均为“#”)

算数表达式求值的算符优先算法。假定输入的表达式语法正确,以’#’做结束符。使用OPTR和OPND分别作为运算符

栈和操作数栈。

expression函数的算法逻辑如下。以下是伪代码,需要用真实的代码代替:

setNull(OPTR); push(OPTR,’#’); //将’#’压入操作符OPTR栈

setNull(OPND);

do{

读入字符ch;

if (ch IN op) //op为运算符的集合

swith( precede(top(OPTR),ch ) { //比较栈顶元素和ch的优先关系

case‘<’:

push(OPTR,ch); //栈顶元素优先级低,则压入操作符栈

读入字符ch;

break;

case‘=’:

if (ch==’)’) x = pop(OPTR); //自己思考什么情况需要这个判断

读入字符ch;

break;

case ‘>’: //栈顶元素优先级高,则取出一个运算符,两个操作数,并进行计算

theta = pop(OPTR);

b= pop(0PND);

a = pop(OPND);

push(OPND,operate(a, theta, b)); //将计算结果压入操作数栈

}

else{

push(OPTR,ch);

}

}while (( ch == ’#’) AND ( top(OPTR) == ‘#’)

return top(OPND);

算法中调用了两个函数,precede是判断运算符栈的栈顶运算符与读入的运算符之间的优先关系;

operate作一元运算:a θb,算出运算结果,例如调用operate(‘1’,’+’,’5’);算出结果6。

举例:利用算法expression对算数表达式3*(7-2)求值,操作过程如下所示:

步骤OPTR栈OPND栈输入字符主要操作

1 # 3*(7-2)# push(OPND,'3')

2 #

3 *(7-2)# push(OPTR,'*')

3 #* 3 (7-2)# push(OPTR,'(')

4 #*( 3 7-2)# push(OPND,'7')

5 #*( 37 -2)# push(OPTR,'-')

6 #*(- 3

7 2)# push(OPND,'2')

7 #*(- 3 7 2 )# push(OPND,operate('7','-','2'))

8 #*( 3 5)# pop(OPTR) //消去一对括号

9 #* 3 5 # push(OPND,operate('3','*','5'))

10 # 15# return(top(OPND))

说明,红色字体+下划线的字母,表示当前正在处理的字符。

四、编程需要思考的几个问题

1.优先级表如何存储并查找?(precede函数)

2.如何判断输入的字符是数字还是运算符?(isOpnd,isOptr函数)

3.根据举例手动跟踪代码,看看能否理解栈的变化情况。

4.如何实现operate函数?

5.目前的算法是在假定表达式正确的情况下进行的,要求大家先这样假定,(1)只对1位整数进行四则运算,采用跟踪调试的方法检验程序的运行。当程序运行正确后,再根据自己的能力情况考虑增加:(2)是否要对空格过滤?(3)是否要对多位整数进行运算?(4)是否需要检查表达式格式是否正确?按照提示的(2)(3)(4)顺序逐步增加功能,每增加一个检查后,用多个范例验证过关后,再进入下一个步骤,不要一次把所有的检查功能都加上去,这样会增加调试难度。

即使只完成(1)的功能也算程序完成,但一定要做到能够想明白程序是如何执行的,堆栈是如何变化的。

五、测试范例

说明:蓝色字体表示输入,绿色字体是说明,不是运行结果的一部分。基本要求完成(1)的效果,如果完全正确完成(1)可以评90。后续其他范例可选,还有其他范例,可以自己思考和检查。

(1)1位整数的表达式

请输入一个正整数表达式(可以输入的字符包括+-*/()#0123456789):

2+3*4*(5-2)+6#

计算结果是:44

(2)包含空格

请输入一个正整数表达式(可以输入的字符包括+-*/()#0123456789):

2 +

3 *9 #

计算结果是:29

请按任意键继续. . .

(3)识别多位整数

请输入一个正整数表达式(可以输入的字符包括+-*/()#0123456789):

23*(2+5)+62/3#

计算结果是:181.667

(2)数字中包含空格

请输入一个正整数表达式(可以输入的字符包括+-*/()#0123456789):

23 *4 5+2#

计算结果是:1037

(4)检查表达式格式是否正确

请输入一个正整数表达式(可以输入的字符包括+-*/()#0123456789):

@345+6#

输入错误,请输入一个正确的表达式(可以输入的字符包括+-*/()#0123456789)。

计算结果是:-1

(4)检查表达式格式是否正确

相关文档
最新文档