Aus Das deutschsprachige Scratch-Wiki
Dieses Tutorial lehrt wie man einen Ausdruck auswertet.
Den benutzerdefinierten Block erstellen
Diese Implementierung enthält nur instabile Unterstützung für Funktionen und gar keine unäre Negation. Deswegen muss man -5 * 10 zu (0-5) * 10 ändern.
Variablen
Man braucht folgende Variablen:
(i)(letzter token)(token)(zeichen)(ergebnis)
Man braucht auf folgende Listen:
(warteschlange::list)(stapel::list)(tokens::list)
Und mache eine Liste "Operatoren" mit genau diesen Zeichen in genau dieser Reinfolge:
- -
- +
- /
- *
- √
Dies ist wichtig, wegen Punkt vor Strich.
Kompletter Benutzerdefinierter Block
Definiere Werte (Ausdruck :: custom-arg) aus
setze [i v] auf (1) // Tokenisierung des Ausdrucks
lösche alles aus [tokens v]
wiederhole (Länge von (Ausdruck :: custom-arg)) mal
setze [zeichen v] auf (Zeichen (i) von (Ausdruck :: custom-arg))
setze [letzter token v] auf (Element (Länge von [tokens v]) von [tokens v])
falls <(123456.7890) enthält (zeichen) ?> , dann
falls <<((letzter token) / (1)) = (last token)> oder <(last token) = [.]>> , dann
ersetze Element (Länge von [tokens v]) von [tokens v] durch (verbinde (letzter token) und (zeichen))
sonst
füge (zeichen) zu [tokens v] hinzu
end
sonst
falls <[()+-/*√] enthält (zeichen) ?> , dann
füge (zeichen) zu [tokens v] hinzu
end
end
ändere [i v] um [1]
end
lösche alles aus [stapel v] // Sortierung der Operationen (Punkt vor Strich, Klammer usw.) und wechselung von Infix zu Postfix
lösche alles aus [warteschlange v]
setze [i v] auf (1)
wiederhole (Länge von [tokens v]) mal
setze [token v] auf (Element (i) von [tokens v])
falls <((token) / (1)) = (token)> , dann
füge (token) zu [warteschlange v] hinzu
end
falls <[operatoren v] enthält (token) ?> , dann
wiederhole bis <<<(Element (1) von [stapel v]) = [(]> oder <(Länge von [stapel v]) = [0]>> oder <(Nummer von (Element (1) von [stapel v]) in [operatoren v]) < (Nummer von (token) in [operatoren v])>>
füge (Element (1) von [stapel v]) zu [warteschlange v] hinzu
lösche (1) aus [stapel v]
end
füge (token) bei (1) in [stapel v] ein
end
falls <(token) = [(]> , dann
füge (token) bei (1) in [stapel v] ein
end
falls <(token) = [)]> , dann
wiederhole bis <<(Element (1) von [stapel v]) = [(]> oder <(Länge von [stapel v]) = [0]>>
füge (Element (1) von [stapel v]) zu [warteschlange v] hinzu
lösche (1) aus [stapel v]
end
falls <(Element (1) von [stapel v]) = [(]> , dann
lösche (1) aus [stapel v]
end
end
ändere [i v] um (1)
end
wiederhole bis <(Länge von [stapel v]) = (0)>
füge (Element (1) von [stapel v]) zu [warteschlange v] hinzu
lösche (1) aus [stapel v]
end
wiederhole bis <(Länge von [warte schlange v]) = [0]>
setze [token v] auf (Element (1) von [warteschlange v])
lösche [1] aus [warteschlange v]
falls <[operatoren v] enthält (token) ?> , dann
falls <(token) = [+]> , dann
füge ((Element [2] von [stapel v]) + (Element [1] von [stapel v])) bei [1] in [stapel v] ein
end
falls <(token) = [-]> , dann
füge ((Element [2] von [stapel v]) - (Element [1] von [stapel v])) bei [1] in [stapel v] ein
end
falls <(token) = [/]> , dann
füge ((Element [2] von [stapel v]) / (Element [1] von [stapel v])) bei [1] in [stapel v] ein
end
falls <(token) = [*]> , dann
füge ((Element [2] von [stapel v]) * (Element [1] von [stapel v])) bei [1] in [stapel v] ein
end
falls <(token) = [√]> , dann // Unärer Operator
füge ([Wurzel v] von (Element [1] von [stapel v])) bei [1] in [stapel v] ein
füge () bei [2] in [stapel v] ein //Füge ein Dummy ein, damit die andere Nummer nicht gelöscht wird, der Token darf nur ein Charakter lang sein. Dieser Weg Funktionen und Unäre Negation einzubauen ist instabil, es wird vorgeschlagen, einen besseren parser zu benutzen.
end
lösche [2] aus [stapel v]
lösche [2] aus [stapel v]
sonst
füge (token) bei (1) in [stapel v] ein
end
end // Löse die Schlange auf
setze [ergebnis v] auf (Element [1] von [stapel v]) // Das Ergebnis ist in der Variable "ergebnis"
Erklärung
Das Problem der Auswertung eines Ausdrucks kann in 3 Schritte unterteilt werden:
- Tokenisierung des Ausdrucks (Umwandlung der Eingabe in eine Liste von Symbolen und Zahlen)
- Umwandlung der Token in das Postfix-Format (wobei der Operator hinter den 2 Operanden steht)
- Auswertung des Postfix-Ausdrucks mit einer Stapelmaschine (Berechnung der Antwort)
Tokenisierung des Ausdrucks
Der Ausdruck muss zunächst in eine Liste umgewandelt werden. Jede Zahl muss alle ihre Ziffern in demselben Listenelement haben.
Dieses Skript tokenisiert einen Ausdruck.
setze [i v] auf (1)
lösche alles aus [tokens v]
wiederhole (Länge von (Ausdruck :: custom-arg)) mal
setze [zeichen v] auf (Zeichen (i) von (Ausdruck :: custom-arg))
setze [letzter token v] auf (Element (Länge von [tokens v]) von [tokens v])
falls <(123456.7890) enthält (zeichen) ?> , dann
falls <<((letzter token) / (1)) = (last token)> oder <(last token) = [.]>> , dann
ersetze Element (Länge von [tokens v]) von [tokens v] durch (verbinde (letzter token) und (letter))
sonst
füge (zeichen) zu [tokens v] hinzu
end
sonst
falls <[()+-/*] enthält (zeichen) ?> , dann
füge (zeichen) zu [tokens v] hinzu
end
end
ändere [i v] um [1]
end
Umwandlung der Token in das Postfix-Format
Zunächst wird der Shunting yard Algorithmus verwendet, um die Token von Infix (A op B) in Postfix (A B op) umzuwandeln. Dies geschieht, weil die Berechnung eines Postfix-Ausdrucks viel weniger Code erfordert als die eines Infix-Ausdrucks.
Dieses Skript lässt den Shunting yard Algorithmus auf der Tokenliste laufen:
lösche alles aus [stapel v]
lösche alles aus [warteschlange v]
setze [i v] auf (1)
wiederhole (Länge von [tokens v]) mal
setze [token v] auf (Element (i) von [tokens v])
falls <((token) / (1)) = (token)> , dann
füge (token) zu [warteschlange v] hinzu
end
falls <[operatoren v] enthält (token) ?> , dann
wiederhole bis <<<(Element (1) von [stapel v]) = [(]> oder <(Länge von [stapel v]) = [0]>> oder <(Nummer von (Element (1) von [stapel v]) in [operatoren v]) < (Nummer von (token) in [operatoren v])>>
füge (Element (1) von [stapel v]) zu [warteschlange v] hinzu
lösche (1) aus [stapel v]
end
füge (token) bei (1) in [stapel v] ein
end
falls <(token) = [(]> , dann
füge (token) bei (1) in [stapel v] ein
end
falls <(token) = [)]> , dann
wiederhole bis <<(Element (1) von [stapel v]) = [(]> oder <(Länge von [stapel v]) = [0]>>
füge (Element (1) von [stapel v]) zu [warteschlange v] hinzu
lösche (1) aus [stapel v]
end
falls <(Element (1) von [stapel v]) = [(]> , dann
lösche (1) aus [stapel v]
end
end
ändere [i v] um (1)
end
wiederhole bis <(Länge von [stapel v]) = (0)>
füge (Element (1) von [stapel v]) zu [warteschlange v] hinzu
lösche (1) aus [stapel v]
end
Berechnen des Ergebnisses mit einer Stapelmaschine
Dieses Skript berechnet das Ergebnis des Postfix-Ausdrucks:
wiederhole bis <(Länge von [warte schlange v]) = [0]>
setze [token v] auf (Element (1) von [warteschlange v])
lösche [1] aus [warteschlange v]
falls <[operatoren v] enthält (token) ?> , dann
falls <(token) = [+]> , dann
füge ((Element [2] von [stapel v]) + (Element [1] von [stapel v])) bei [1] in [stapel v] ein
end
falls <(token) = [-]> , dann
füge ((Element [2] von [stapel v]) - (Element [1] von [stapel v])) bei [1] in [stapel v] ein
end
falls <(token) = [/]> , dann
füge ((Element [2] von [stapel v]) / (Element [1] von [stapel v])) bei [1] in [stapel v] ein
end
falls <(token) = [*]> , dann
füge ((Element [2] von [stapel v]) * (Element [1] von [stapel v])) bei [1] in [stapel v] ein
end
falls <(token) = [*]> , dann
füge (([Wurzel v] von (Element [1] von [stapel v])) ) bei [1] in [stapel v] ein
end
lösche [2] aus [stapel v]
lösche [2] aus [stapel v]
sonst
füge (token) bei (1) in [stapel v] ein
end
end // Löse die Schlange auf
setze [ergebnis v] auf (Element [1] von [stapel v])
Wie man den Block benutzt
Wenn die grüne Flagge angeklickt werte [5 + 5] aus :: custom sage (ergebnis)
Wenn es funktioniert hat, sollte die Figur die Nummer 10 anzeigen.
Demo
[wiki=de:Mathematische Ausdrücke auswerten]Mathematische Ausdrücke auswerten[/wiki]