Сначала создайте отдельную функцию, которая будет генерировать список токенов из вашей строки. Токен - это число (без знака) или один символ:
def tokenize(s):
s = re.sub(r"\s+", "", s)
result = []
while (s):
if s[0] in "0123456789":
number = s[0]
s = s[1:]
while (s and s[0] in "0123456789."):
number += s[0]
s = s[1:]
result.append(number)
if s:
result.append(s[0])
s = s[1:]
else:
result.append(s[0])
s = s[1:]
return result
Тогда вам нужно будет отслеживать унарные операции плюс и минус. Для этого мы вводим специальную операцию 'neg' - когда вы обрабатываете эту операцию в постфиксной нотации, вы просто отрицаете значение в верхней части стека операндов.
Вы ожидаете одинарные операции плюс и минус в начале строки или сразу после открытия '('. После обработки числового операнда или закрытия ')' вы сбрасываете унарный флаг в False, потому что унарный плюс или минус не могут появляться в этих позициях. Когда унарный флаг равен true, вы должны отслеживать входящие «+» и «-», используйте для него логический флаг «neg». Измените состояние 'neg' на каждом '-'. Когда вы, наконец, найдете операнд - проверьте состояние флага 'neg'. Если это правда, то вам нужно поставить нашу специальную операцию 'neg' после операнда. Помещение операции 'neg' после закрытия ')' немного сложно и требует использования opStack.
def infixToPostfix(infixexpr):
prec = {}
prec["^"] = 3
prec["*"] = 3
prec["/"] = 3
prec["+"] = 2
prec["-"] = 2
prec["("] = 1
prec["neg"] = 1
opStack = Stack()
postfixList = []
tokenList = tokenize(infixexpr)
print(tokenList)
unary = True
neg = False
for token in tokenList:
if unary and token in "+-":
if token == '-':
neg = not neg
elif isNumber(token):
postfixList.append(token)
if neg:
postfixList.append("neg")
neg = False
unary = False
elif token == '(':
if neg:
opStack.push("neg")
neg = False
opStack.push(token)
unary = True
elif token == ')':
topToken = opStack.pop()
unary = False
while topToken != '(':
postfixList.append(topToken)
topToken = opStack.pop()
if not opStack.isEmpty() and opStack.peek() == "neg":
postfixList.append(opStack.pop())
else:
while (not opStack.isEmpty()) and \
(prec[opStack.peek()] >= prec[token]):
postfixList.append(opStack.pop())
opStack.push(token)
while not opStack.isEmpty():
postfixList.append(opStack.pop())
return " ".join(postfixList)
Ввод:
"-(( 1 + 3 ) ) * 4 - ( -9.2 - 0 ) * ( 5 + 8 ) - 4 * (-2)"
Выход:
1 3 + neg 4 * 9.2 neg 0 - 5 8 + * - 4 2 neg * -
ОБНОВЛЕНИЕ 2020-03-12
Если вы хотите обрабатывать отрицательные числа как один отрицательный операнд, а не как положительный операнд, за которым следует операция 'neg', тогда вы просто нужна очень маленькая модификация метода infixToPostfix. Вам нужно только изменить ветку elif isNumber(token)
. Я поставлю это здесь полностью, хотя:
def infixToPostfix(infixexpr):
prec = {}
prec["^"] = 3
prec["*"] = 3
prec["/"] = 3
prec["+"] = 2
prec["-"] = 2
prec["("] = 1
prec["neg"] = 1
opStack = Stack()
postfixList = []
tokenList = tokenize(infixexpr)
unary = True
neg = False
for token in tokenList:
if unary and token in "+-":
if token == '-':
neg = not neg
elif isNumber(token):
if neg:
postfixList.append("-" + token)
else:
postfixList.append(token)
neg = False
unary = False
elif token == '(':
if neg:
opStack.push("neg")
neg = False
opStack.push(token)
unary = True
elif token == ')':
topToken = opStack.pop()
unary = False
while topToken != '(':
postfixList.append(topToken)
topToken = opStack.pop()
if not opStack.isEmpty() and opStack.peek() == "neg":
postfixList.append(opStack.pop())
else:
while (not opStack.isEmpty()) and \
(prec[opStack.peek()] >= prec[token]):
postfixList.append(opStack.pop())
opStack.push(token)
while not opStack.isEmpty():
postfixList.append(opStack.pop())
return " ".join(postfixList)
Теперь вывод равен
1 3 + neg 4 * -9.2 0 - 5 8 + * - 4 -2 * -
ОБНОВЛЕНИЕ 2020-03-13
В В исходном посте я поставил следующее предложение:
Вы ожидаете унарные операции плюс и минус в начале строки или сразу после открытия '('.
код там и в предыдущем обновлении также отражает это. Я знал, что это технически не совсем правильно. Унарную операцию можно ожидать и после операции. Однако я не хотел разрешать выражения типа 2+--+-+3
, поэтому я исключил возможность для унарных операций после операции. К сожалению, это также исключает возможность для 2^-3
. Если вы хотите иметь возможность разбирать выражения, такие как 2^-3
, то вам просто нужно разрешить унарную операцию после другой операции, она требует добавления одиночная строка unary = True
в ветви else
:
else:
while (not opStack.isEmpty()) and \
(prec[opStack.peek()] >= prec[token]):
postfixList.append(opStack.pop())
opStack.push(token)
unary = True # This is the only new line
Теперь вы можете анализировать 2^-3
как 2^(-3)
. Однако он также позволяет анализировать 2+-3
как 2+(-3)
. I всегда в и последняя возможность очень уродливая в компьютерных языках, но если это хорошо для вас - хорошо. Конечно, вы также можете разрешить разбор унарной операции только после ^
, но не после других операций. Это потребует проверки текущего токена и установки unary
в значение «Истина», только если токен находится в списке операций, допускающих после него одинарный минус.