简体   繁体   中英

Evaluating postfix in python?

I want to write a fucnction to evaluate a postfix expression passed as a list. So far I have got:

def evalPostfix(text):
    s = Stack()
    for symbol in text:
        if symbol in "0123456789":
            s.push(int(symbol))
        if not s.is_empty():
            if symbol == "+":
                plus = s.pop() + s.pop()
            if symbol == "-":
                plus = s.pop() - s.pop()
            if symbol == "*":
                plus = s.pop() * s.pop()
            if symbol == "/":
                plus = s.pop() / s.pop()

But I think I have the wrong approach. Help?

You have a few problems:

  1. You are discarding the value after you come across an operator. To fix this you have to push the result of any operator back to the stack and then proceed to the next step.
  2. You do not skip the rest of the logic when come across a number (it is not going to make your code return a wrong answer, but still is not very smart)
  3. Your function does not return anything.

Something like this should work:

def eval_postfix(text):
    s = list()
    for symbol in text:
        if symbol in "0123456789":
            s.append(int(symbol))

        plus = None
        elif not s.is_empty():
            if symbol == "+":
                plus = s.pop() + s.pop()
            elif symbol == "-":
                plus = s.pop() - s.pop()
            elif symbol == "*":
                plus = s.pop() * s.pop()
            elif symbol == "/":
                plus = s.pop() / s.pop()
        if plus is not None:
            s.append(plus)
        else:
             raise Exception("unknown value %s"%symbol)
    return s.pop()

Here is a solution that may work for you. I've tried to change your code as little as possible.

Change #1: Rather than check whether symbol is between 0 and 9, you might simply try to convert symbol (which starts as a string ) to an int . If that succeeds, you can treat symbol as an operand. (This allows your code to handle multi-digit numbers.)

Change #2: Raise an error if a non-number, non-operator occurs in text . (You don't want anything else to be in there.)

Change #3: Use eval instead of writing out each of the operators. eval comes with a lot of safety concerns, but I think here, since we're making sure everything is a number or an operator, we're OK.

Change #4: Push intermediate results into the stack.

Change #5: Return s.pop() after the list has been exhausted. You might want to add a line that confirms that s contains just one value at this point.

Caveat: Note that this code assumes that s will contain two values every time an operator is encountered. You might want to catch the error that would be raised if this were not true with another try / except pair of statements.

def evalPostfix(text):
    s = Stack()
    for symbol in text:
        try:
            result = int(symbol)
        except ValueError:
            if symbol not in '+-*/':
                raise ValueError('text must contain only numbers and operators')
            result = eval('%d %s %d' % (s.pop(), symbol, s.pop()))
        s.push(result)
    return s.pop() 

Hosane has provided a nicely detailed answer to your question, but there's one error that i think i see, although i'll admit im not an expert on this subject.

Since you're using pop your calculation goes like this. (last number in stack) (operator) (second last number in stack) for example if you have ["3","2","+"] in your list, you get 3+2

this is fine for addition or multiplication, but if you take division or substraction this would result in the wrong answer. for example (3-2) in post fix would be [3,2,-]. Your code would calculate this as (2-3) when it should have been (3-2).

So you should change the division and substraction if cases to;

elif symbol=="-":
        s.append(-stack.pop() + stack.pop())
elif symbol=="/":
        s.append(1/stack.pop() * stack.pop())

Working code that is translation of K&R C example,

def eval_postfix(text):

    stack = []
    tokens = text.split(" ")

    for token in tokens:

        if token.strip() == '':
            continue 

        elif token == "+":
            stack.append(stack.pop() + stack.pop())

        elif token == "-":
            op2 = stack.pop() 
            stack.append(stack.pop() - op2)

        elif token == '*':
            stack.append(stack.pop() * stack.pop())

        elif token == '/':
            op2 = stack.pop()
            if op2 != 0.0:
                stack.append(stack.pop() / op2)
            else:
                raise ValueError("division by zero found!")

        elif (is_number(token)):
                stack.append(float(token))

        else:
            raise ValueError("unknown token {0}".format(token))


    return stack.pop()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM