Ich wechsel gerade von Java zu Python und habe die Aufgabe übernommen, zu versuchen, einen Taschenrechner zu erstellen, der symbolische Operationen an Infix-notierten mathematischen Ausdrücken durchführen kann (ohne die Verwendung von benutzerdefinierten Modulen wie Sympy). Derzeit ist er so aufgebaut, dass er Zeichenketten akzeptiert, die durch Leerzeichen getrennt sind, und nur die (, ), +, -, *, und / Operatoren ausführen kann. Leider kann ich den grundlegenden Algorithmus zur Vereinfachung symbolischer Ausdrücke nicht herausfinden.
Zum Beispiel sollte mein Programm bei der Eingabe der Zeichenkette '2 * ( ( 9 / 6 ) + 6 * x )' die folgenden Schritte durchführen:
- 2 * ( 1.5 + 6 * x )
- 3 + 12 * x
Aber ich bekomme das Programm nicht dazu, das x zu ignorieren, wenn die 2 verteilt wird. Zusätzlich, wie kann ich 'x * 6 / x' behandeln, so dass es nach der Vereinfachung '6' zurückgibt?
EDIT: Zur Klärung, mit "symbolisch" meinte ich, dass es Buchstaben wie "A" und "f" im Ergebnis belassen wird, während die übrigen Berechnungen durchgeführt werden.
EDIT 2: Ich habe den Code (weitgehend) fertiggestellt. Ich poste ihn hier, falls jemand in Zukunft auf diesen Beitrag stößt, oder wenn einer von euch neugierig war.
def reduceExpr(useArray):
# Verwende das native eval() von Python zum Berechnen, wenn keine Buchstaben erkannt werden.
if (not hasLetters(useArray)):
return [calculate(useArray)] # Unterscheidet sich von eval(), da der String-Version des Ergebnisses zurückgibt
# Basisfall. Gibt useArray zurück, wenn die Liste nur ein String enthält.
if (len(useArray) == 1):
return useArray
# Basisfall. Gibt die mit Leerzeichen verbundenen Elemente von useArray als Liste mit einem String zurück.
if (len(useArray) == 3):
return [' '.join(useArray)]
# Überprüft, ob Klammern im Ausdruck vorhanden sind und setzt.
# Zählt die Anzahl der Klammern und behält die Position der ersten gefundenen ( bei.
parentheses = 0
leftIdx = -1
# Dieser Try/Except-Block ist im Grunde ein If/Else-Block. Da useArray.index('(') einen KeyError auslöst,
# wenn '(' in useArray nicht gefunden werden kann, wird die nächste Zeile nicht ausgeführt und das Klammerpaar wird nicht erhöht.
try:
leftIdx = useArray.index('(')
parentheses += 1
except Exception:
pass
# Wenn ein KeyError zurückgegeben wurde, ist leftIdx = -1 und rightIdx = parentheses = 0.
rightIdx = leftIdx + 1
while (parentheses > 0):
if (useArray[rightIdx] == '('):
parentheses += 1
elif (useArray[rightIdx] == ')'):
parentheses -= 1
rightIdx += 1
# Wenn das Klammernpaar nicht leer ist, führt es den Inhalt erneut durch; sonst entfernt es die Klammern.
if (leftIdx > -1 and rightIdx - leftIdx > 2):
return reduceExpr(useArray[:leftIdx] + [' '.join(['(',reduceExpr(useArray[leftIdx+1:rightIdx-1])[0],')'])] + useArray[rightIdx:])
elif (leftIdx > -1):
return reduceExpr(useArray[:leftIdx] + useArray[rightIdx:])
# Wenn der Operator + oder - ist, behalte die ersten beiden Elemente und verarbeite den Rest der Liste zuerst
if isAddSub(useArray[1]):
return reduceExpr(useArray[:2] + reduceExpr(useArray[2:]))
# Andernfalls, wenn der Operator * oder / ist, verarbeite zuerst die ersten 3 Elemente, dann den Rest der Liste
elif isMultDiv(useArray[1]):
return reduceExpr(reduceExpr(useArray[:3]) + useArray[3:])
# Habe dies nur eingefügt, damit der Compiler nicht beschwert, dass die Funktion kein Return hat (da sie von einer anderen Funktion aufgerufen wurde).
return None
8 Stimmen
Ich glaube, du fängst an, die Tugend von Sympy zu erkennen :-) Ich denke, du überlegst, einen vollwertigen rekursiven Abstiegsparser für arithmetische Ausdrücke zu erstellen, gefolgt von der Manipulation des Datensbaums, um X zu lösen.
0 Stimmen
@li.davidm Es befindet sich noch in den logischen Phasen. Ich kann nicht herausfinden, wie ich über den ersten Stolperstein hinaus implementieren soll.
0 Stimmen
@wberry Ja, ich weiß, dass es rekursiv sein muss, sonst könnte ich keine verschachtelten Klammern verarbeiten. Das wurde bereits implementiert. Außerdem war ich wohl nicht deutlich genug, denn ich soll x als x stehen lassen, nicht versuchen, x zu definieren.
0 Stimmen
Dein Beispiel ist falsch. Das Ergebnis sollte
3 + 2 * 6 * x = 3 + 12 * x
sein. Abgesehen davon, muss ich fragen: Versuchst du symbolische Mathematik zu betreiben, oder versuchst du einfach arithmetische Ausdrücke auszuwerten, ohne auf Variablen zu stoßen?0 Stimmen
Selbst wenn ich mit einem funktionierenden Parser beginne, der mir freie Variablen-Token in einer Baumstruktur gibt, ist es wahrscheinlich eine Abendarbeit, daraus einen Abschluss zu machen, der entweder einen Ausdruck zurückgibt (wenn es mehr freie Variablen gibt) oder einen Wert, wenn ein Wert gegeben wird. Aber das ist der Ansatz, den ich wählen würde. Viel Glück!
3 Stimmen
Übrigens kann "x * 6 / x" eigentlich nicht auf 6 reduziert werden, weil es undefiniert ist, wenn x == 0.
0 Stimmen
@delnan Ähm...ich glaube, ich habe die Definition von symbolischer Mathematik missverstanden. Ich versuche nur, den Ausdruck auszuwerten, ohne an den Variablen zu scheitern. Und Danke für das Aufzeigen des Fehlers.
0 Stimmen
Bewirbst du dich nur bei justin.tv?
0 Stimmen
@Swiss Ja, aber ich habe in den letzten 3 Stunden darüber nachgedacht und konnte es nicht herausfinden. Ich glaube, ich weiß jetzt, wie es geht. Da @wberry darauf hingewiesen hat, dass ich mir keine Gedanken über die Vereinfachung von x / x machen muss, muss ich nur * und / über ()-begrenzte Ausdrücke verteilen, während ich + und - zu den ()-begrenzten Ausdrücken hinzufüge. Ich werde es versuchen und sehen, ob es funktioniert.
0 Stimmen
Bist du auf einen endlichen Satz von Variablen (x,y) beschränkt oder kann der Eingang eine beliebige zuvor unbekannte Zeichenfolge enthalten? Wenn du Variablen kennst, habe ich etwas wirklich einfaches und leichtes.
0 Stimmen
@phkahler Es kann sich um einen beliebigen Buchstaben von a-z, groß oder klein, handeln.
0 Stimmen
@wberry: x * 6 / x ist definiert als 6, wenn lim x->0. Dies kann mit Hilfe der Regel von L'Hospital überprüft werden.
0 Stimmen
@Swiss Ich stimme zu, dass lim [x->0] (x * 6 / x) == 6 ist. Das bedeutet jedoch nicht, dass die Ausdrücke identisch sind. 0 * 6 / 0 ist offensichtlich undefiniert, und daher gilt auch für x * 6 / x, wenn x == 0.
0 Stimmen
@wberry: Nein, das ist inkorrekt.