简体   繁体   English

堆栈计算器(后缀,Python)

[英]Stack calculator (postfix, Python)

The question I'm having problem on is calculating the postfix form expressions: for example, (1, 2, '+', 3, '*').我遇到的问题是计算后缀形式的表达式:例如,(1, 2, '+', 3, '*')。

By calculating the expression using the following algorithm: 1. If the expression only contains an integer, return that integer.通过使用以下算法计算表达式: 1. 如果表达式仅包含一个整数,则返回该整数。 2. Otherwise, maintain a stack. 2. 否则,保持一个堆栈。 Loop through the tuple and push every element to the stack.循环遍历元组并将每个元素推入堆栈。 If the element is an operator, pop the first two element out of the stack, calculate the result and push the result into the stack.如果元素是运算符,则将前两个元素从堆栈中弹出,计算结果并将结果压入堆栈。 To illustrate, we take the example above.为了说明,我们以上面的例子为例。 Initially, the stack is empty.最初,堆栈是空的。 The test case is测试用例是

calculate((1, 2, '*', 3, '-', 2, '*', 5, '+'))  
3

whereas my first code was no good (hardcoded and all >< ):而我的第一个代码不好(硬编码和所有 >< ):

def calculate(inputs):
    if len(inputs) ==1:
        return inputs[0]
    elif len(inputs) == 5:
        s= []
        push_stack(s, inputs[0])
        push_stack(s, inputs[1])
        if inputs[2] == '*':
            A = s.pop() * s.pop()
        elif inputs[2] == '+':
            A = s.pop() + s.pop()
        elif inputs[2] == '-':
            A= s.pop() - s.pop()
        elif inputs[2] == '/':
            A = s.pop() / s.pop()
        s.clear()
        s= [A]
        push_stack(s, inputs[3])
        if inputs[4] == '*':
            A = s.pop() * s.pop()
        elif inputs[4] == '+':
            A = s.pop() + s.pop()
        elif inputs[4] == '-':
            A= s.pop() - s.pop()
        elif inputs[4] == '/':
            A = s.pop() / s.pop()
        return A
    else:
        s= []
        push_stack(s, inputs[0])
            push_stack(s, inputs[1])
        if inputs[2] == '*':
            A = s.pop() * s.pop()
        elif inputs[2] == '+':
            A = s.pop() + s.pop()
        elif inputs[2] == '-':
            A= s.pop() - s.pop()
        elif inputs[2] == '/':
            A = s.pop() / s.pop()
        s.clear()
        s= [A]
        push_stack(s, inputs[3])
        if inputs[4] == '*':
            A = s.pop() * s.pop()
        elif inputs[4] == '+':
            A = s.pop() + s.pop()
        elif inputs[4] == '-':
            A= s.pop() - s.pop()
        elif inputs[4] == '/':
            A = s.pop() / s.pop()
        s.clear()
        s= [A]
        push_stack(s, inputs[5])
        if inputs[6] == '*':
            A = s.pop() * s.pop()
        elif inputs[6] == '+':
            A = s.pop() + s.pop()
        elif inputs[6] == '-':
            A= s.pop() - s.pop()
        elif inputs[6] == '/':
            A = s.pop() / s.pop()
        s.clear()
        s= [A]
        push_stack(s, inputs[7])
        if inputs[8] == '*':
            A = s.pop() * s.pop()
        elif inputs[8] == '+':
            A = s.pop() + s.pop()
        elif inputs[8] == '-':
            A= s.pop() - s.pop()
        elif inputs[8] == '/':
            A = s.pop() / s.pop()
        return A

Sorry for making you read that!抱歉让你读到那个! Then I changed the style to然后我将样式更改为

def calculate(inputs):
    if len(inputs) ==1:
        return inputs[0]
    else:
        s =[]
        for i in inputs:
            if type(i) == int:
                return push_stack(s, i)
            elif i is '*' or '/' or 'x' or '+':
                A = s.pop()
                B =s.pop()
                know = operator(i, A, B)
                C = push_stack(s, know)
        return C

def operator(sign, one, two):
    if sign == '*':
        A = one * two
    elif sign == '+':
        A = one + two
    elif sign == '-':
        A= one - two
    elif sign == '/':
        A = one / two
    return A

Am I getting closer to the idea and how does my code improve?我是否越来越接近这个想法以及我的代码如何改进?

** Edit ** Using IDLE: ** 编辑 ** 使用空闲:

>>> calculate((1, 2, '*', 3, '-'))
[1]
>>> calculate((1, 2, '+', 3, '*'))
[1]
>>> calculate((1, 2, '*', 3, '-', 2, '*', 5, '+'))
[1]

which is not the answer I'm looking for.这不是我正在寻找的答案。 It should be 1 then 9 then 3.它应该是 1 然后是 9 然后是 3。

There is a problem with有问题

elif i is '*' or '/' or 'x' or '+':

which is treated as被视为

elif (i is '*') or ('/') or ('x') or ('+'):

which is not what you want (it's always true).这不是您想要的(总是如此)。 You can instead use something like:您可以改为使用以下内容:

elif i in ('*', '/', 'x', '+'):

Also:还:

if type(i) == int:
    return push_stack(s, i)

You shouldn't be returning there.你不应该回到那里。 You simply want:你只是想要:

if type(i) == int:
    push_stack(s, i)

Lastly, I think a better approach would be to always use the stack, and just return the top of the stack at the end of your function.最后,我认为更好的方法是始终使用堆栈,并在函数末尾返回堆栈顶部。 This saves you from having to create a special case for 1-element arguments to calculate() .这使您不必为calculate() 1 元素参数创建特殊情况。 Putting this all together, something along the lines of this should work:将所有这些放在一起,应该可以工作:

def calculate(inputs):
    stack = []
    for a in inputs:
        if type(a) is int:
            stack.append(a)
            continue

        op1, op2 = stack.pop(), stack.pop()

        if a == '+':
            stack.append(op2 + op1)
        elif a == '-':
            stack.append(op2 - op1)
        elif a == '*':
            stack.append(op2 * op1)
        elif a == '/':
            stack.append(op2 / op1)

    return stack.pop()

Right now this does no error-checking (eg for malformed expressions), but it's easy to add.现在这不会进行错误检查(例如,格式错误的表达式),但是很容易添加。

Part of the problem, which you seem to have realized, is that you are trying to make one function do everything.您似乎已经意识到的部分问题是,您正试图让一个函数做所有事情。 If you take a look at what the code is doing, and try to separate out the responsibilities, you end up with something like the following:如果您查看代码正在做什么,并尝试分离职责,您最终会得到如下内容:


There are some crucial differences between Python 2.x and 3.x. Python 2.x 和 3.x 之间存在一些重要差异。 Where these differences would cause problems, it's easiest to introduce helper functions and define them appropriately for each version:在这些差异会导致问题的地方,最容易引入辅助函数并为每个版本适当地定义它们:

import sys
if sys.hexversion < 0x3000000:
    # Python 2.x
    is_str = lambda s: isinstance(s, basestring)
    inp = raw_input
else:
    # Python 3.x
    is_str = lambda s: isinstance(s, str)
    inp = input

You do a fair bit of stack maintenance;你做了一些堆栈维护; this can be avoided by making a Stack class which knows how to pop and push multiple items.这可以通过创建一个知道如何弹出和推送多个项目的 Stack 类来避免。 (It should also help your out-of-order arguments problem; 4 2 - should be 4 - 2, not 2 - 4) (它也应该有助于您的乱序参数问题; 4 2 -应该是 4 - 2,而不是 2 - 4)

class Stack(list):
    def pop_n(self, n):
        """
        Pop n items off stack, return as list
        """
        assert n >= 0, "Bad value {}: n cannot be negative".format(n)
        if n == 0:
            return []
        elif n <= len(self):
            res = self[-n:]
            del self[-n:]
            return res
        else:
            raise ValueError("cannot pop {} items, only {} in stack".format(n, len(self)))

    def push_n(self, n, items):
        """
        Push n items onto stack
        """
        assert n == len(items), "Expected {} items, received {}".format(n, len(items))
        self.extend(items)

Rather than having a single "do-any-operation" function, we can let each operator have its own self-contained code.我们可以让每个操作符拥有自己独立的代码,而不是拥有一个单一的“任意操作”功能。 This makes it much easier to change or add operators without funny side-effects.这使得在没有有趣的副作用的情况下更容易更改或添加运算符。 First we make an Operator class首先我们创建一个 Operator 类

class Op:
    def __init__(self, num_in, num_out, fn):
        """
        A postfix operator

        num_in:     int
        num_out:    int
        fn:         accept num_in positional arguments,
                    perform operation,
                    return list containing num_out values
        """
        assert num_in  >= 0, "Operator cannot have negative number of arguments"
        self.num_in = num_in
        assert num_out >= 0, "Operator cannot return negative number of results"
        self.num_out = num_out
        self.fn = fn

    def __call__(self, stack):
        """
        Run operator against stack (in-place)
        """
        args = stack.pop_n(self.num_in)         # pop num_in arguments
        res = self.fn(*args)                    # pass to function, get results
        stack.push_n(self.num_out, res)         # push num_out values back

then we define the actual operators as然后我们将实际的运算符定义为

ops = {
    '*':  Op(2, 1, lambda a,b: [a*b]),          # multiplication
    '/':  Op(2, 1, lambda a,b: [a//b]),         # integer division
    '+':  Op(2, 1, lambda a,b: [a+b]),          # addition
    '-':  Op(2, 1, lambda a,b: [a-b]),          # subtraction
    '/%': Op(2, 2, lambda a,b: [a//b, a%b])     # divmod (example of 2-output op)
}

Now that the support structure is in place, your evaluation function is simply现在支持结构已经到位,您的评估功能很简单

def postfix_eval(tokens):
    """
    Evaluate a series of tokens as a postfix expression;
    return the resulting stack
    """
    if is_str(tokens):
        # if tokens is a string, treat it as a space-separated list of tokens
        tokens = tokens.split()

    stack = Stack()
    for token in tokens:
        try:
            # Convert to int and push on stack
            stack.append(int(token))
        except ValueError:
            try:
                # Not an int - must be an operator
                # Get the appropriate operator and run it against the stack
                op = ops[token]
                op(stack)         # runs Op.__call__(op, stack)
            except KeyError:
                # Not a valid operator either
                raise ValueError("unknown operator {}".format(token))
    return stack

and to make it easier to test, we can make it interactive:为了更容易测试,我们可以使它具有交互性:

def main():
    while True:
        expr = inp('\nEnter a postfix expression (or nothing to quit): ').strip()
        if expr:
            try:
                print("  => {}".format(postfix_eval(expr)))
            except ValueError as error:
                print("Your expression caused an error: {}".format(error))
        else:
            break

if __name__=="__main__":
    main()

This runs like这就像

Enter a postfix expression (or nothing to quit): 1 2 * 3 -
=> [-1]
Enter a postfix expression (or nothing to quit): 1 2 + 3 *
=> [9]
Enter a postfix expression (or nothing to quit): 1 2 * 3 - 2 * 5 +
=> [3]
Enter a postfix expression (or nothing to quit): 5 2 /%
=> [2, 1]

If you are doing calculator, or something like that, you should use function eval() .如果你在做计算器或类似的东西,你应该使用函数eval() It will return result of expression.它将返回表达式的结果。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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