Поменяйте местами токены и используйте стековый компьютер, как показано ниже:
def prefix_eval(tokens):
stack = []
for t in reversed(tokens):
if t == '+': stack[-2:] = [stack[-1] + stack[-2]]
elif t == '-': stack[-2:] = [stack[-1] - stack[-2]]
elif t == '*': stack[-2:] = [stack[-1] * stack[-2]]
elif t == '/': stack[-2:] = [stack[-1] / stack[-2]]
else: stack.append(t)
assert len(stack) == 1, 'Malformed expression'
return stack[0]
>>> prefix_eval(['+', 2, 2])
4
>>> prefix_eval(['-', '*', 3, 7, '/', 20, 4])
16
Обратите внимание, что stack[-1]
и stack[-2]
поменялись местами по сравнению с обычным стековым компьютером.Это делается для того, чтобы учесть тот факт, что это действительно префиксная нотация в обратном порядке.
Я должен объяснить несколько идиом Python, которые я использовал:
stack = []
: нет встроенногов стеке в Python, но списки легко вызывать для той же цели. stack[-1]
и stack[-2]
: Python поддерживает отрицательные индексы.stack[-2]
относится ко второму последнему элементу списка. stack[-2:] = ...
: это назначение объединяет два идиома в дополнение к отрицательной индексации: - Slicing:
A[x:y]
относится ко всемэлементы от A
от x
до y
, включая x
, но исключая y
(например, A [3: 5] относится к элементам 3 и 4).Пропущенное число подразумевает либо начало, либо конец списка.Поэтому stack[-2:]
относится к каждому элементу от второго до последнего конца списка, т. Е. К двум последним элементам. - Назначение слайса: Python позволяет назначать слайс, что имеет эффектсклеивания нового списка вместо элементов, на которые ссылается срез.
Собрав все вместе, stack[-2:] = [stack[-1] + stack[-2]]
складывает вместе последние два элемента стека, создаетодноэлементный список из суммы, и назначает этот список на срез, состоящий из двух чисел.Чистый эффект заключается в замене двух верхних чисел в стеке их суммой.
Если вы хотите начать со строки, простой внешний синтаксический анализатор сделает свое дело:
def string_eval(expr):
import re
return prefix_eval([t if t in '+-*/' else int(t)
for t in re.split(r'\s+', expr)])
>>> string_eval('/ 15 - 6 3')
5