这是一个Python后缀表示法解释器,它使用堆栈来计算表达式。是否可以使此功能更有效和准确?
#!/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()
答案 0 :(得分:9)
一般建议:
has_key()
长期以来一直被弃用,以支持in
运营商:改用它。python -m cProfile -s cumulative foo.py
具体要点:
list
makes a good stack开箱即用。特别是,它允许您使用切片表示法(tutorial)来一步替换pop
/ pop
/ append
舞蹈。 ARITHMETIC_OPERATORS
可以直接引用运算符实现,而不需要getattr
间接。把所有这些放在一起:
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()
答案 1 :(得分:3)
列表可以直接用作堆栈:
>>> stack = []
>>> stack.append(3) # push
>>> stack.append(2)
>>> stack
[3, 2]
>>> stack.pop() # pop
2
>>> stack
[3]
您也可以将操作符函数直接放入ARITHMETIC_OPERATORS字典:
ARITHMETIC_OPERATORS = {"+":operator.add,
"-":operator.sub,
"*":operator.mul,
"/":operator.div,
"%":operator.mod,
"**":operator.pow,
"//":operator.floordiv}
然后
if operators.has_key(val):
method = operators[val]
这些目标不是为了提高效率(尽管它可能会产生这种效果),而是让它们对读者更加明显。摆脱不必要的间接和包装层次。这将使您的代码更少被混淆。它还将提供(微不足道的)性能改进,但除非你测量它,否则不要相信。
顺便说一句,使用列表作为堆栈是相当common惯用的Python。
答案 2 :(得分:2)
您可以直接映射运算符:{"+": operator.add, "-": operator.sub, ...}
。这更简单,不需要不必要的getattr
,并且还允许添加其他功能(不会破坏运算符模块)。
你也可以删除一些只用过一次的临时变量:
rhs, lhs = stack.pop(), stack.pop()
stack.push(operators[val](lhs, rhs)).
此外(较少的性能和更多的样式问题,也是主观的),我可能根本不在循环中进行错误处理并将其包装在一个带有except KeyError
块的try块中( “未知操作数”),except IndexError
块(空堆栈),...
但准确吗?它会给出错误的结果吗?