Processing Arithmetic Expressions
Application: Arithmetic expressions
One application of stacks is the translation and evaluation of arithmetic expressions.
infix, postfix, and prefix notation
Our usual method of writing arithmetic expressions is called infix notation. This is because a binary operator (like '+') is written in between its two operands (as in "a + b"). There are two alternative forms of notation used in certain situations. One is prefix notation, in which an operator is written before its operands. In prefix notation, the sum of a and b is written "+ a b". This is the notation used to write function calls in mathematics and computer science. It is also used in the Lisp and Scheme programming languages. In postfix notation, an operator is written after its operands. The sum of a and b is written "a b +". (You may have seen this as "reverse Polish notation".) Postfix notation forms the conceptual basis for the way that arithmetic expressions are evaluated by a computer. One important characteristic of both postfix and prefix notations is that they are unambiguous; no parentheses are needed to indicate the order of operations.example Evaluate the prefix expression + * 2 3 / 10 2
example Evaluate the postfix expression 10 3 5 + 6 2 * + *
example Translate the infix expression "a + b * ((c - d - e * f) + g)" to prefix notation.
example Translate the infix expression "a + b * ((c - d - e * f) + g)" to postfix notation.
An expression written in postix notation can be evaluated by a simple algorithm using a stack. As a result, the usual strategy for evaluating an infix expression is to first translate it into postfix and then evaluate the postfix expression. The same strategy is commonly used by compilers in translating expressions into machine code. First, translate the infix expression into postfix, then translate the postfix into machine code.
Postfix evaluation algorithm (It is assumed that we are given a method "getToken" which gets the next token from the input stream. The token may be either an operator or an operand.) Because operators are read in after their operands, the algorithm uses a stack to hold operands which are waiting to be operated on.
Stack s = new Stack(); // create an empty stack as a work area
while there are more tokens {
token = getToken();
if(token is an operand)
s.push(token);
else { // token is an operator
rightOperand = s.pop();
leftOperand = s.pop();
result = apply token to leftOperand and rightOperand;
s.push(result);
}
}
// at this point, the stack should contain one item, which is the value of the expression
value = s.pop();
exercise Write a stack-based algorithm to evaluate an expression written in prefix notation.
A stack is also used by the algorithm which is used to translate an infix expression into postfix notation. This algorithm is more complicated because it has to incorporate the notion of operator precedence. Operator precedence is one technique used to deal with the inherent ambiguity of infix expressions. For example, how should the expression
5 + 6 * 7
be evaluated? Do we add 5 and 6, getting 11, then multiply by 7 , getting 77? Or do we first multiply 6 and 7 and add the product to 5, getting 47? We do the multiplication first because of the mathematical convention that multiplication has precedence over addition.
As another example, consider the expression
10 - 7 - 2
Two subtractions are indicated. Which is performed first? By mathematical convention, if two consecutive operators have equal precedence, they are performed left-to-right, so the correct result is 1, not 5.
These two conventions remove the ambiguity, but they lead to another question: What if I really want to multiply the sum of 5 and 6 by 7, or subtract the difference of 7 and 2 from 10. That is, is there a way to override the operator precedence rule or the left-to-right rule? In this case, the mathematical convention is that these rules can be overridden by using parentheses. I can write the expressions as
( 5 + 6 ) * 7
and
10 - ( 7 - 2 )
An algorithm to translate an infix expression into postifix notation needs to implement all of these rules. The following algorithm does so by assigning a numerical precedence to each binary operator. It uses a stack to hold operands which are waiting for their operators and operators which are waiting for their second operand.
Infix-to-postfix algorithm.
String postfix = new String("");
Stack s = new Stack();
while(there are more tokens){
token = getToken();
if token is an operand
postfix.append(token);
else {
while ( s.top() has higher precedence than token )
postfix.append( s.pop() );
s.push(token);
}
}
while(s is not empty)
postfix.append( s.pop() );
This version of the algorithm handles operator precedence and left-to-right associativity, but not parentheses. How can it be modified to implement parenthesization of expressions?
Here's a modified version which also handles parentheses:
String postfix = new String("");
Stack s = new Stack();
while(there are more tokens){
token = getToken();
if token is an operand
postfix.append(token);
else if token is '('
s.push(token);
else if token is ')'{
while( s.top() is not '(' )
postfix.append( s.pop() );
s.pop(); // pop the matching left parenthesis; the right paren is never pushed
}
else {
while ( s.top() has higher precedence than token )
postfix.append( s.pop() );
s.push(token);
}
}
while(s is not empty)
postfix.append( s.pop() );