表达式求值问题:
表达式求值是程序设计语言编译中一个最基本的问题。它的实现也需要栈的运用。下面介绍的算法是由运算符优先级确定运算顺序的对表达式求值算法。
21:36:29
一个表达式是由运算数(operand)、运算符(operator)和界限符(delimiter)组成的有意义的式子。一般地,运算数既可以是常量又可以是被说明的变量或常量标识符。运算符从运算数的个数上分,有单目运算符和双目运算符;从运算类型上分,有算术运算符、关系运算符和逻辑运算符。界限符主要是指左右括号和表达式结束符。在此仅限于讨论只含常量的双目运算的算术表达式。
假设所讨论的算术运算符包括:+、-、、/、%、^(乘方)和括号()。
设运算规则为:
(1)运算符的优先级为:()→ ^ → 、/、%→ +、-;
(2)有括号出现时先算括号内的,后算括号外的,对于多层括号,由内向外进行;
(3)乘方连续出现时先算最右面的。
可以将表达式作为一个满足表达式语法规则的串来存储,如32^(4+22-1*3)-5。
为实现表达式求值,需要设置两个栈:一个称为运算符栈OPTR,用以寄存运算符;另一个称为运算数栈OPND,用以寄存运算数和运算结果。求值的处理过程是:自左至右扫描表达式的每一个字符,当扫描到运算数时,就将其压入栈OPND;当扫描到运算符时,若这个运算符比OPTR栈顶运算符的优先级高则入栈OPTR,继续向后处理,若这个运算符比OPTR栈顶运算符的优先级低则从OPND栈中弹出两个运算数,从OPTR栈中弹出栈顶运算符进行运算,并将运算结果压入栈OPND,继续处理当前字符,直到遇到结束符为止。
根据运算规则,左括号“(”在栈外时它的级别最高,而进栈后它的级别则最低了;乘方运算的结合性是自右向左,所以,它的栈外级别高于栈内。也就是说,有的运算符栈内、栈外的优先级别是不同的。当遇到右括号“)”时,一直需要对OPTR栈进行出栈操作,并弹出相应的操作数,做对应的运算,直到遇到栈顶为左括号“(”将其出栈为止。
OPND栈初始化为空,为了使表达式中的第一个运算符入栈,OPTR栈中预设一个最低级的运算符“(”。根据以上分析,每个运算符的栈内、栈外优先级别如下:

以上算法的基本思想是:
(1)首先置OPND栈为空栈,表达式起始符“(”为OPTR栈的栈底元素;
(2)依次读入表达式中的每个字符,若是运算数则进OPND栈,若是运算符则和OPTR栈的栈顶运算符比较优先级后作相应操作,直至整个表达式求值完毕(即OPTR栈的栈顶元素为“(”且当前读入的字符为“#”)。
算法3.2 表达式求值算法。

OperandType EvaluateExpression( )
{ / * 算术表达式求值的算符优先算法,设OPTR和OPND分别为运算符栈和运算数栈 * / 
/ * OP为运算符集合 * /
Init_Stack (OPTR); Push_Stack (OPTR, ‘(’ );
Init_Stack (OPND); c=getchar( );
while ( c!=’#’ )
{ if (!In (c, OP)) / * 不是运算符则进栈 * /
{ Push_Stack (OPND,c); c=getchar(); }
else
switch (Precede(GetTop(OPTR),c))
case ‘<’: / * 栈顶元素优先权低 */
Push_Stack (OPTR,c); c=getchar();
break;
case ‘=’: / * 脱括号并接受下一字符 */
Pop_Stack (OPTR,x); c=getchar();
break;
case ‘>’: / * 退栈并将运算结果入栈 */
Pop_Stack (OPTR, theta);
Pop_Stack (OPND, b);
Pop_Stack (OPND, a);
Push_Stack (OPND, Operate(a, theta, b));
break;
} / * Switch */
}
/* while*/
return GetTop(OPND);
} / * EvaluateExpression*/
算法3.2中还调用了两个函数。其中,Precede是判定OPTR栈的栈顶运算符与读入的运算符之间优先关系的函数;Operate为进行二元运算a θ b的函数,如果是编译表达式,则产生这个运算的一组相应指令并返回存放结果的中间变量名;如果是解释执行表达式,则直接进行该运算,并返回运算的结果。
表达式32^(4+22-13)-5求值过程中两个栈的状态情况如表3.1所示。
在实际编译程序中,为了处理方便,常常首先把表达式转换成等价的后缀表达式。所谓后缀表达式,是指将运算符放在运算数之后的表达式。在后缀表达式中,不再需要括号,所有的计算按运算符出现的顺序严格地从左向右进行,而不用再考虑运算符的级别和运算规则。如表达式32^(4+22-13)-5的后缀表达式为:3 2 4 2 2 +1 3 * - ^ * 5 - 。
表3.1 表达式32^(4+22-13)-5的求值过程