Can this Python postfix notation (reverse polish notation) interpreter be made more efficient and accurate?

余生长醉 提交于 2019-11-28 21:58:44

General suggestions:

  • Avoid unnecessary type checks, and rely on default exception behavior.
  • has_key() has long been deprecated in favor of the in operator: use that instead.
  • Profile your program, before attempting any performance optimization. For a zero-effort profiling run of any given code, just run: python -m cProfile -s cumulative foo.py

Specific points:

  • list makes a good stack out of the box. In particular, it allows you to use slice notation (tutorial) to replace the pop/pop/append dance with a single step.
  • ARITHMETIC_OPERATORS can refer to operator implementations directly, without the getattr indirection.

Putting all this together:

ARITHMETIC_OPERATORS = {
    '+':  operator.add, '-':  operator.sub,
    '*':  operator.mul, '/':  operator.div, '%':  operator.mod,
    '**': operator.pow, '//': operator.floordiv,
}

def postfix(expression, operators=ARITHMETIC_OPERATORS):
    stack = []
    for val in expression.split():
        if val in operators:
            f = operators[val]
            stack[-2:] = [f(*stack[-2:])]
        else:
            stack.append(int(val))
    return stack.pop()

Lists can be used directly as stacks:

>>> stack = []
>>> stack.append(3) # push
>>> stack.append(2)
>>> stack
[3, 2]
>>> stack.pop() # pop
2
>>> stack
[3]

You can also put the operator functions directly into your ARITHMETIC_OPERATORS dict:

ARITHMETIC_OPERATORS = {"+":operator.add,
                        "-":operator.sub,
                        "*":operator.mul,
                        "/":operator.div, 
                        "%":operator.mod,
                        "**":operator.pow,
                        "//":operator.floordiv}

then

if operators.has_key(val):
    method = operators[val]

The goal of these is not to make things more efficient (though it may have that effect) but to make them more obvious to the reader. Get rid of unnecessary levels of indirection and wrappers. That will tend to make your code less obfuscated. It will also provide (trivial) improvements in performance, but don't believe that unless you measure it.

Incidentally, using lists as stacks is fairly common idiomatic Python.

You can directly map the operators: {"+": operator.add, "-": operator.sub, ...}. This is simpler, doesn't need the unnecessary getattr and also allows adding additional functions (without hacking the operator module). You could also drop a few temporary variables that are only used once anyway:

rhs, lhs = stack.pop(), stack.pop()
stack.push(operators[val](lhs, rhs)).    

Also (less of a performance and more of a style issue, also subjective), I would propably don't do error handling at all in the loop and wrap it in one try block with an except KeyError block ("Unknown operand"), an except IndexError block (empty stack), ...

But accurate? Does it give wrong results?

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!