Shunting-yard-Algorithmus
Der Shunting-yard-Algorithmus (deutsch: Rangierbahnhof-Algorithmus) ist eine Methode, um mathematische Terme von der Infixnotation in die umgekehrte polnische Notation oder in einen abstrakten Syntaxbaum zu überführen. Der Algorithmus wurde von Edsger W. Dijkstra erfunden und mit englisch shunting yard ‚Rangierbahnhof‘ benannt, weil er in seiner Arbeitsweise an einen Rangierbahnhof erinnert.
Funktionsweise
[Bearbeiten | Quelltext bearbeiten]Der Shunting-yard-Algorithmus benötigt für die Konversion sowohl eine Input- als auch eine Outputqueue sowie einen Stack, der für das Zwischenspeichern der Operatoren benötigt wird. Der Inputstring wird zeichenweise gelesen, wobei alle Zahlen direkt und in derselben Reihenfolge in die Ausgabevariable geschrieben werden. Falls das anstehende Zeichen ein Operationszeichen ist, wird es auf den Operatorenstack gelegt. Falls bereits ein Operator auf dem Stack liegt, wird anhand der Operatorrangfolge und der Operatorassoziativität entschieden, ob der neue Operator direkt auf den Stack gelegt wird oder ob der Stack zuerst in den Output geleert wird.
Öffnende Klammern werden ebenfalls auf den Operatorstack gelegt, allerdings werden sie beim Entfernen nicht in den Outputstream geschrieben. Bei schließenden Klammern wird der Stack bis zum Antreffen einer öffnenden Klammer geleert; sollte keine öffnende Klammer gefunden werden, ist der Inputstring unvollständig, wobei allerdings die Fehlerbehandlung nicht durch den Algorithmus definiert wird.
Im Detail
[Bearbeiten | Quelltext bearbeiten]Es folgt ein deutscher Pseudocode, der in die meisten Programmiersprachen einfach übersetzt und angepasst werden kann.
Stack (mit LIFO-Prinzip) sowie Ausgabequeue (mit FIFO-Prinzip) anlegen. SOLANGE Tokens verfügbar sind: Token einlesen. WENN Token IST-Zahl: Token ZU Ausgabe. ENDEWENN WENN Token IST-Funktion: Token ZU Stack. ENDEWENN WENN Token IST-Argumenttrennzeichen: BIS Stack-Spitze IST öffnende-Klammer: Stack-Spitze ZU Ausgabe. FEHLER-BEI Stack IST-LEER: GRUND (1) Ein falsch platziertes Argumenttrennzeichen. GRUND (2) Der schließenden Klammer geht keine öffnende voraus. ENDEFEHLER ENDEBIS ENDEWENN WENN Token IST-Operator SOLANGE Stack IST-NICHT-LEER UND Stack-Spitze IST Operator UND (Präzedenz von Token IST-KLEINER Präzedenz von Stack-Spitze ODER Präzedenz von Token IST-GLEICH Präzedenz von Stack-Spitze UND Token IST-linksassoziativ) Stack-Spitze ZU Ausgabe. ENDESOLANGE Token ZU Stack. ENDEWENN WENN Token IST öffnende-Klammer: Token ZU Stack. ENDEWENN WENN Token IST schließende-Klammer: BIS Stack-Spitze IST öffnende-Klammer: FEHLER-BEI Stack IST-LEER: GRUND (1) Der schließenden Klammer geht keine öffnende voraus. ENDEFEHLER Stack-Spitze ZU Ausgabe. ENDEBIS Stack-Spitze (öffnende-Klammer) entfernen WENN Stack-Spitze IST-Funktion: Stack-Spitze ZU Ausgabe. ENDEWENN ENDEWENN ENDESOLANGE BIS Stack IST-LEER: FEHLER-BEI Stack-Spitze IST öffnende-Klammer: GRUND (1) Es gibt mehr öffnende als schließende Klammern. ENDEFEHLER Stack-Spitze ZU Ausgabe. ENDEBIS
Dieser Algorithmus setzt voraus, dass alle Tokens vom Parser richtig erkannt werden und gültig sind. Insbesondere die Überlagerung (Vorzeichen versus Operator) der Zeichen „+“ und „−“ übernimmt dieser Algorithmus nicht. Ein Konflikt von rechts- und linksassoziativen Operatoren mit gleicher Präzedenz wird nicht abgefangen.
Der „lesende“ Zugriff auf den Stack (z. B. bei „Stack-Spitze IST“) und der „nehmende“ (bei „Stack-Spitze ZU“) werden vorausgesetzt.
Vorausgesetzte Funktionen sind:
- Erkennen von (vorzeichenbehafteten) Zahlen
- Erkennen von Funktionen
- Erkennen von Argumenttrennzeichen
- Erkennen von Operatoren
- Feststellen der Operatorassoziativität
- Feststellen der Operatorpräzedenz (hier bedeutet höhere Präzedenz eine stärkere Bindung), die Präzedenz einer Funktion ist maximal
Beispiele
[Bearbeiten | Quelltext bearbeiten]Die folgenden in Infixnotation gegebenen Rechnungen sollen umgeformt werden. Es wird dokumentiert, was geschieht.
Präzedenzen: (+
,−
) < (·
,:
) < (^
) < Funktionen
(3 + 4)(5 − 6)
[Bearbeiten | Quelltext bearbeiten]- Tokens zusammenfassen:
(
3
+
4
)
(
5
−
6
)
(Hier: Jedes Zeichen ist ein Token) (
auf den Stack3
zur Ausgabe+
auf den Stack4
zur Ausgabe)
:+
zur Ausgabe(
vom Stack entfernen
- Operator erwartet, aber
(
gefunden:- implizites
·
auf den Stack (
auf den Stack
- implizites
5
zur Ausgabe−
auf den Stack6
zur Ausgabe)
:−
zur Ausgabe(
vom Stack entfernen
- Ende:
·
zur Ausgabe
Ausgabe:3
4
+
5
6
−
·
1 + 2 − 3 · 4 + 5^6^7 · 8 − 9
[Bearbeiten | Quelltext bearbeiten]- Tokens zusammenfassen:
1
+
2
−
3
·
4
+
5
^
6
^
7
·
8
−
9
(Hier: Jedes Zeichen ist ein Token) 1
zur Ausgabe+
auf den Stack2
zur Ausgabe−
:+
zur Ausgabe−
auf den Stack
3
zur Ausgabe·
auf den Stack4
zur Ausgabe+
:·
zur Ausgabe−
zur Ausgabe+
auf den Stack
5
zur Ausgabe^
auf den Stack6
zur Ausgabe^
auf den Stack (^
ist rechtsassoziativ)7
zur Ausgabe·
:^
zur Ausgabe^
zur Ausgabe·
auf den Stack
8
zur Ausgabe−
:·
zur Ausgabe+
zur Ausgabe−
auf Stack
9
zur Ausgabe- Ende:
−
zur Ausgabe
Ausgabe: 1
2
+
3
4
·
−
5
6
7
^
^
8
·
+
9
−
Explizit geklammert ([(1 + 2) − (3·4)] + {[5^(6^7)]·8}) − 9 ist dies möglicherweise einfacher nachzuvollziehen.
cos(1 + sin(ln(5) − exp(8))^2)
[Bearbeiten | Quelltext bearbeiten]- Tokens zusammenfassen:
cos
(
1
+
sin
(
ln
(
5
)
−
exp
(
8
)
)
^
2
)
cos
auf den Stack(
auf den Stack1
zur Ausgabe+
auf den Stacksin
auf den Stack(
auf den Stackln
auf den Stack(
auf den Stack5
zur Ausgabe)
:- oberste
(
vom Stack entfernen ln
zur Ausgabe
- oberste
–
auf den Stackexp
auf den Stack(
auf den Stack8
zur Ausgabe)
:- oberste
(
vom Stack entfernen exp
zur Ausgabe
- oberste
)
:–
zur Ausgabe- oberste
(
vom Stack entfernen sin
zur Ausgabe
^
auf den Stack2
zur Ausgabe)
:^
zur Ausgabe+
zur Ausgabe- oberste
(
vom Stack entfernen cos
zur Ausgabe
- Ende: Keine übrigen Elemente im Stack, also fertig!
Ausgabe: 1
5
ln
8
exp
−
sin
2
^
+
cos
Weblinks
[Bearbeiten | Quelltext bearbeiten]- Ursprüngliche Beschreibung des Algorithmus (PDF; 1,1 MB; englisch)