1

I'm trying to implement this right-to-left evaluation algorithm of a postfix expression but I can't seem to get it to work.

for each token in the reversed postfix expression:
  if token is an operator:
    push token onto the operator stack
    pending_operand ← False
  else if token is an operand:
    operand ← token
    if pending_operand is True:
      while the operand stack is not empty:
        operand_1 ← pop from the operand stack
        operator ← pop from the operator stack
        operand ← evaluate operator with operand_1 and operand
    push operand onto the operand stack
    pending_operand ← True
result ← pop from the operand stack

From wikipedia.

This is how the steps are illustrated:

15 7 1 1 + − ÷ 3 × 2 1 1 + + − =
15 7 1 1 + − ÷ 3 × 2     2 + − =
15 7 1 1 + − ÷ 3 ×         4 − =
15 7     2 − ÷ 3 ×         4 − =
15         5 ÷ 3 ×         4 − =
             3 3 ×         4 − =
                 9         4 − =
                             5

I don't really get how this follows from the algorithm. I keep getting the wrong answer trying to evaluate the expression 15 7 1 1 + − ÷ 3 × 2 1 1 + + − (should be 5). I've spent hours trying to get it working in assembly and I tried manually going through it but I keep getting the wrong answer. I think part of if lies in operate(operand_1, operand), ruled out. Anyways, I threw together this piece of JavaScript to show my interpretation of the algorithm, since it's way clearer than assembly.

const   rpn = [15, 7, 1, 1, '+', '-', '/', 3, '*', 2, 1, 1, '+', '+', '-'];
const   operator_Stack = [];
const   operand_Stack = [];
let     pending = false;

for (i = rpn.length - 1; i >= 0; i--) {
    const   token = rpn[i];
    if (typeof token === "string") {
        operator_Stack.push(token);
        pending = false;
    } else {
        let operand = token;
        if (pending) {
            while (operand_Stack.length > 0) {
                let operand_1 = operand_Stack.pop();
                let operator = operator_Stack.pop();
                let expr = operand + " " + operator + " " + operand_1;
                console.log(expr);
                operand = eval(expr);
            }       
        }
        operand_Stack.push(operand);
        pending = true;
    }
}
console.log("The expression evaluates to: " + operand_Stack.pop());

This evaluates the following expression in the following order:

"1 + 1"
"2 + 2"
"1 + 1"
"2 - 3"
"-1 / 4"
"7 * -0.25"
"15 - -1.75"

The first three evaluations appear to be correct. Then things start to go wrong.

As a binary tree 15 7 1 1 + − ÷ 3 × 2 1 1 + + − would look like this

            [-]
           /   \
         [*]    [+]
        / \    /   \
      [/] [3] [2]  [+]
      / \          /  \
   [15] [-]      [1]  [1]
        / \
     [7]  [+]
         /   \
       [1]   [1]            

The correct order of evaluation should be:

1 + 1
2 + (1 + 1)
1 + 1
7 - (1 + 1)
15 / (7 - 2)
3 * (15 / 5)
9 - 4

To me, my JavaScript code implements the algorithm as it's stated. Yet obviously it's not correct. As I see there are two possibilities, the algorithm is wrong or, more likely, my interpretation is. Problem is, I can't figure out which of the two it is.

What is it that I'm missing?

CervEd
  • 121

1 Answers1

2

You have a somewhat complicated program, which produces an unexpected output. It seems to work correctly in the first steps, but somewhere it goes wrong. Where? First thing you do, it to reduce the input to the simplest possible which still produces an error.

Manually removing sub-expression, I ended up with:

rpn = [7, 2, '-'];

This is 7 - 2 and should obviously result in 5. But the program actually produces -5. A possible explanation for this could be it actually inverts the operands, since 2 - 7 = -5. This hypothesis is easy to test now by using division. E.g. [10, 2, '/'] is 10/2 which is 5, but if we run it we get 0.2 which happen to be 2/10. So clearly the operands are switched. Now look at the line in the algorithm:

operand ← evaluate operator with operand_1 and operand

This line is perhaps ambiguous since it doesn't explicitly specify the order of operands. But operand_1 is the one which is first popped which means the one last pushed. In other words, in [7, 2, '-'], operand is 2 and operand_1 is 7. In your implementation you have operand_1 <operator> operand which is reverse of what you expect.

The reason you don't see the error in the first three iterations is that the order of the operands doesn't matter for +.

After checking with the operands reversed, there seem to be a second problem which is with the algorithm itself. Consider this input:

[15, 7, 2, '-', '/', 3, '*']

This should correspond to 15 / (7-2) * 3 which should be 9, but the program returns 25. It seems it evaluates as (7-2) / 3 * 15.

According to the algorithm as stated, after resolving 7 2 -, it should then continue resolving from the stack until the operand stack is empty, and only then continue to process 15. Since 3 is on the operand stack at this point, you get 5 / 3 which is wrong as far as I can tell.

Wikipedia does not provide a reference for the algorithm, but looking at the edit history of the page, it seems someone edited the fist line from "reversed prefix expression" to "reversed postfix expression". Possibly there have been some confusion because reversing a prefix notation as a whole is not the same as postfix aka reverse polish.

Consider this infix notation:

(2+2)/(7-2)*3

In prefix notation (aka Polish):

* / + 2 2 - 7 2 3

In postfix notation (aka reverse Polish):

2 2 + 7 2 - / 3 *

Note that this is different from just reversing the prefix notation as a whole! But Wikipedia seem to mix this up. I also note the algorithm specified by Wikipedia for resolving prefix notation is the same as the left-to-right algorithm for postfix notation. But since these are not the same as shown above, I believe the algorithm is wrong in the second context.

Reverse polish notation is not really amenable to right-to-left processing. If you want to use right-to-left processing you should consider using prefix notation as the input.

JacquesB
  • 61,955
  • 21
  • 135
  • 189