首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >这个Python postfix notation (反向波兰语notation)解释器可以变得更高效、更准确吗?

这个Python postfix notation (反向波兰语notation)解释器可以变得更高效、更准确吗?
EN

Stack Overflow用户
提问于 2010-10-06 00:59:23
回答 3查看 7K关注 0票数 7

这是一个Python postfix符号解释器,它利用一个堆栈来计算表达式。有没有可能让这个功能更高效、更准确?

代码语言:javascript
复制
#!/usr/bin/env python


import operator
import doctest


class Stack:
    """A stack is a collection, meaning that it is a data structure that 
    contains multiple elements.

    """

    def __init__(self):
        """Initialize a new empty stack."""
        self.items = []       

    def push(self, item):
        """Add a new item to the stack."""
        self.items.append(item)

    def pop(self):
        """Remove and return an item from the stack. The item 
        that is returned is always the last one that was added.

        """
        return self.items.pop()

    def is_empty(self):
        """Check whether the stack is empty."""
        return (self.items == [])


# Map supported arithmetic operators to their functions
ARITHMETIC_OPERATORS = {"+":"add", "-":"sub", "*":"mul", "/":"div", 
                        "%":"mod", "**":"pow", "//":"floordiv"}


def postfix(expression, stack=Stack(), operators=ARITHMETIC_OPERATORS):
    """Postfix is a mathematical notation wherein every operator follows all 
    of its operands. This function accepts a string as a postfix mathematical 
    notation and evaluates the expressions. 

    1. Starting at the beginning of the expression, get one term 
       (operator or operand) at a time.
       * If the term is an operand, push it on the stack.
       * If the term is an operator, pop two operands off the stack, 
         perform the operation on them, and push the result back on 
         the stack.

    2. When you get to the end of the expression, there should be exactly 
       one operand left on the stack. That operand is the result.

    See http://en.wikipedia.org/wiki/Reverse_Polish_notation

    >>> expression = "1 2 +"
    >>> postfix(expression)
    3
    >>> expression = "5 4 3 + *"
    >>> postfix(expression)
    35
    >>> expression = "3 4 5 * -"
    >>> postfix(expression)
    -17
    >>> expression = "5 1 2 + 4 * + 3 -"
    >>> postfix(expression, Stack(), ARITHMETIC_OPERATORS)
    14

    """
    if not isinstance(expression, str):
        return
    for val in expression.split(" "):
        if operators.has_key(val):
            method = getattr(operator, operators.get(val))
            # The user has not input sufficient values in the expression
            if len(stack.items) < 2:
                return
            first_out_one = stack.pop()
            first_out_two = stack.pop()
            operand = method(first_out_two, first_out_one)
            stack.push(operand)
        else:
            # Type check and force int
            try:
                operand = int(val)
                stack.push(operand)
            except ValueError:
                continue
    return stack.pop()


if __name__ == '__main__':
    doctest.testmod()
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2010-10-06 02:18:17

一般建议:

  • 避免不必要的类型检查,并依赖默认的异常操作。长期以来,in操作符一直受到抨击:在尝试任何性能优化之前,请使用
    • in程序。对于任何给定代码的零工作量分析运行,只需运行:python -m cProfile -s cumulative foo.py

具体要点:

  • list makes a good stack开箱即用。特别是,它允许您使用切片表示法( pop/pop/append indirection.

tutorial)用单个step.

  • ARITHMETIC_OPERATORS可以直接引用运算符实现,而不使用dance

把所有这些放在一起:

代码语言:javascript
复制
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()
票数 10
EN

Stack Overflow用户

发布于 2010-10-06 01:11:26

列表可以直接用作堆栈:

代码语言:javascript
复制
>>> stack = []
>>> stack.append(3) # push
>>> stack.append(2)
>>> stack
[3, 2]
>>> stack.pop() # pop
2
>>> stack
[3]

您还可以将运算符函数直接放入ARITHMETIC_OPERATORS字典中:

代码语言:javascript
复制
ARITHMETIC_OPERATORS = {"+":operator.add,
                        "-":operator.sub,
                        "*":operator.mul,
                        "/":operator.div, 
                        "%":operator.mod,
                        "**":operator.pow,
                        "//":operator.floordiv}

然后

代码语言:javascript
复制
if operators.has_key(val):
    method = operators[val]

这样做的目的不是为了让事情变得更有效率(尽管它可能会有这样的效果),而是为了让读者更容易理解它们。去掉不必要的间接层和包装器。这将会使你的代码不那么模糊。它还将在性能方面提供(微不足道的)改进,但除非您进行了测试,否则不要相信这一点。

顺便说一句,使用列表作为堆栈是common惯用的Python。

票数 3
EN

Stack Overflow用户

发布于 2010-10-06 01:27:17

您可以直接映射操作符:{"+": operator.add, "-": operator.sub, ...}。这更简单,不需要不必要的getattr,还允许添加额外的功能(不需要修改操作员模块)。您还可以删除一些只使用一次的临时变量:

代码语言:javascript
复制
rhs, lhs = stack.pop(), stack.pop()
stack.push(operators[val](lhs, rhs)).    

另外(不是性能问题,更多是风格问题,这也是主观的),我可能根本不会在循环中进行错误处理,而是将其包装在一个带有except KeyError块(“未知操作数”)、except IndexError块(空堆栈)的try块中……

但是准确吗?它会给出错误的结果吗?

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/3865939

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档