有没有更好的方法来实现这一点?

2024-06-16 14:04:46 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在用Python编写一个计算器(作为练习),其中有一点我很好奇。在

程序将输入拆分为一系列数字和运算符。然后计算结果:

import operator
ops = {'+' : operator.add,                      # operators and corresponding functions
       '-' : operator.sub,
       '*' : operator.mul,
       '/' : operator.truediv,
       '%' : operator.mod}
precedence = [['*', '/', '%'], ['+', '-']]      # order of precedence for operators

def evaluate(exp):
  for oplist in precedence:                     # search for operators first in order of precedence
    for op in exp:                              # then from left to right
      if op in oplist:
        index = exp.index(op)
        result = ops[op](exp[index - 1], exp[index + 1])
                                                # compute the result of the operation
        exp[index - 1:index + 2] = [result]
                                                # replace operation and operands with result
  return exp[0]

# for example,
evaluate([2, '+', 3, '+', 4, '+', 5])
# should return 14

此函数按优先级降低的顺序在列表中查找算术运算符,然后从左到右进行查找,当找到这样的运算符时,它将调用相邻列表元素(操作数)上的相应函数,并用运算结果替换列表中的运算符和操作数。一旦执行了所有的操作,列表将包含一个元素-计算的结果。在

但是,这个函数并没有按照预期的方式运行。问题是(我认为)这个函数在遍历列表时会修改列表(通过分配给切片)。我已经找到了这个问题的解决方案here(每次修改列表时都重新启动内部for循环),但是给出解决方案的人似乎认为应该有更好的方法来完成所需的任何事情。在

我想知道是否有更好的方法来实现这个算法,以避免奇怪的“重启循环”的事情。在

谢谢你的建议!在


Tags: andof函数in列表forindexorder
3条回答

我不知道你说的“重启循环”是什么意思。在这种特殊情况下,我认为您应该简单地将一个函数重复应用于表达式,直到它被还原为一个值。这比它可能的效率要低,但它是有效的,而且是明确的。所以。。。在

def find_op(tokens, oplist):
    for i, token in enumerate(tokens):
        if token in oplist:
            return i
    else:
        return -1

def reduce_binary_infix(tokens, i, ops):
    op = ops[tokens[i]]
    tokens[i - 1: i + 2] = [op(tokens[i - 1], tokens[i + 1])]
    return tokens

def evaluate(tokens, ops, precedence):
    for prec in precedence:
        index = find_op(tokens, prec)
        while index >= 0:
            tokens = reduce_binary_infix(tokens, index, ops)
            index = find_op(tokens, prec)
    return tokens

print evaluate([2, '+', 3, '+', 4, '+', 5], ops, precedence)

测试:

^{pr2}$

通过不重复搜索整个字符串,可以提高效率。这可以通过在find_op中添加一个start_index参数,并通过让reduce_binary_infix返回一个新的当前索引和缩减的列表来实现。在

更重要的是它的可读性,而不是它的可读性。在

如果使用while循环,则可以操作索引

def evaluate(exp):
    for oplist in precedence:  # search for operators first in order of precedence
        idx = 0
        while idx < len(exp):
            op = exp[idx]
            if op in oplist:
                result = ops[op](exp[idx - 1], exp[idx + 1])
                exp[idx - 1:idx + 2] = [result]
                idx -= 1       # move index back since list is shortened by 2
            else:
                idx += 1
    return exp[0]

我想我应该换一种方式来做,使用递归函数。弹出操作并用评估结果替换它们。在

import operator

ops = {
    '+' : operator.add,
    '-' : operator.sub,
    '*' : operator.mul,
    '/' : operator.truediv,
    '%' : operator.mod,
}

precedence = [
    set(['*', '/', '%']),
    set(['+', '-']),
]

def evaluate(expr):
    # if len == 3 then just return result of expression
    if len(expr) == 3:
        l, op, r = expr
        return ops[op](l, r)
    else:
        for op_list in precedence:
            for op in expr:
                if op in op_list:
                    # find index of first operation
                    idx = expr.index(op)-1
                    # pop off and evaluate first matching operation in expr
                    result = evaluate([expr.pop(idx) for i in range(3)])
                    # insert result back into expr
                    expr.insert(idx, result)
                    return evaluate(expr)

相关问题 更多 >