Sie können den Algorithmus des Rangierbahnhofs so ändern, dass er in einem einzigen Durchgang analysiert und ausgewertet wird. Der Zwischenschritt der Konvertierung in Postfix entfällt. Die Beschränkung des Problems auf nur die vier mathematischen Standardoperatoren ohne Klammern ändert den grundlegenden Ansatz nicht.
Hier ist der Standard-Shunting-Yard-Algorithmus, aus dem verknüpften Artikel. Ich habe unten als Referenz Zeilennummern hinzugefügt:
1: while there are tokens to be read:
2: read a token.
3: if the token is a number, then push it to the output queue.
4: if the token is an operator, then:
5: while (there is an operator at the top of the operator stack with
6: greater precedence) or (the operator at the top of the operator stack has
7: equal precedence and
8: the operator is left associative) and
9: (the operator at the top of the stack is not a left bracket):
10: pop operators from the operator stack, onto the output queue.
11: push the read operator onto the operator stack.
12: if the token is a left bracket (i.e. "("), then:
13: push it onto the operator stack.
14: if the token is a right bracket (i.e. ")"), then:
15: while the operator at the top of the operator stack is not a left bracket:
16: pop operators from the operator stack onto the output queue.
17: pop the left bracket from the stack.
/* if the stack runs out without finding a left bracket, then there are
18: mismatched parentheses. */
19: if there are no more tokens to read:
20: while there are still operator tokens on the stack:
21: /* if the operator token on the top of the stack is a bracket, then
22: there are mismatched parentheses. */
23: pop the operator onto the output queue.
24: exit.
Die Modifikationen beinhalten die Ausgabewarteschlangen mit einem Operandenstapel ersetzt: ein Stapel von ganzen Zahlen. Wenn Sie beispielsweise in Zeile 3 die Nummer nicht in die Ausgabewarteschlange schieben, drücken Sie die Nummer auf den Operandenstapel. Dies erfordert natürlich, dass Sie das Zeichen in eine Ganzzahl konvertieren.
Dann auf der Linie 10, wo Sie Pop-Betreiber aus dem Operator-Stack und drückt, um die Ausgabewarteschlange, Sie stattdessen:
- einen Operator Pop
- Pop zwei Operanden aus dem Operandenstapel
- Perform der Betrieb
- das Ergebnis wieder auf den Operandenstapel schieben
Sie Linien ersetzen 16 und 23 mit dem gleiche Art von Logik.
Wenn Sie zu irgendeinem Zeitpunkt beim Auswerten nicht genügend Operanden auf dem Operandenstapel haben, dann haben Sie nicht übereinstimmende Operatoren: etwas wie 2 + 3 + - (außer Sie entscheiden sich für unäre + und unäre -), oder 2 */3.
Wenn Sie Zeile 24 erreicht haben, ist der Operatorstapel leer und der Operandenstapel sollte einen einzelnen Wert enthalten: das Endergebnis. Wenn mehr als ein Element auf dem Operandenstapel ist, dann haben Sie zu viele Operanden (sollte nicht passieren).
Ersetzen Sie also die Zeile 24 durch einen Popup aus dem Operandenstapel, und Sie können das als Ergebnis zurückgeben.
Der Shunting-Yard ist eigentlich eine sehr vereinfachte Version des "rekursiven lexikalischen Parsers", die Sie erwähnt haben. Anstatt Stack-Frames durch Rekursion zu erstellen, werden die Stacks direkt manipuliert.
Das Ändern Ihres Codes sollte nicht zu schwierig sein. Unten ist eine modifizierte Version Ihrer convertToPostfix
-Funktion, genannt evaluateInfix
, die alles in einem einzigen Durchlauf erledigt. Beachten Sie, dass ich nicht ein Python-Programmierer bin, so werde ich den Code nicht garantieren einwandfrei funktionieren, aber es sollte Ihnen die Idee geben:
def evaluateInfix(self, infix: list) -> int:
operatorStack = collections.deque()
operandStack = collections.deque()
result = []
for item in infix:
if item.isdigit():
val = convertDigitToNumber(item) // however that's done
// push to operand stack
operandStack.append(val)
else:
while len(operatorStack) > 0 and self.has_higher_precedence(operatorStack[-1], item):
// pop two operands from stack, evaluate, and push result
// call this "pop and eval"
op2 = operandStack[-1]
operandStack.pop()
op1 = operandStack[-1]
operandStack.pop()
operator = operatorStack[-1]
operatorStack.pop()
val = evaluate(operator, op1, op2) // this function evaluates op1 <operator> op2
// push result back onto operand stack
operandStack.append(val)
operatorStack.append(item)
while len(operatorStack) > 0:
// insert "pop and eval" code here
// at this point there should be a single value on the operand stack
return operandStack[-1]
Die evaluate
Funktion einen Operator und zwei Operanden macht, macht sie den Betrieb und gibt das Ergebnis zurück. So gegeben ('+', 3, 5)
, würde es 3+5
berechnen und 8
zurückgeben.
Die Idee hier ist, dass, wenn Sie auswerten, Sie zwei Operanden und einen einzigen Operator nehmen.
Testfall: 3+5*2
.
operand operator
char stack stack
--------------------------
3 [3] []
+ [3] [+]
5 [3,5] [+]
* [3,5] [+,*]
2 [3,5,2] [+,*]
<end>
at this point, we're at the end of the string.
Pop 2 and 5 from the operand stack, pop * from the operator stack,
and evaluate
[3,10] [+]
pop and eval again
[13] []
operator stack is empty. Pop result from operand stack and return.
Der [Shunting-yard-Algorithmus] (https://en.wikipedia.org/wiki/Shunting-yard_algorithm) ist die Standardmethode, um dieses Problem zu lösen. Es gibt keine Klammern in Ihren Gleichungen, so dass Sie diesen Teil des Algorithmus auslassen können, wenn Sie vereinfachen möchten. –
@JimMischel Ich denke mein Code ist im Grunde eine Implementierung des Rangierbahnhof-Algorithmus. –
Ohne Klammern brauchst du nicht einmal einen Stack, endlicher Speicher ist genug. –