Mein Formelparser aus BPS #20

Übersicht BlitzMax, BlitzMax NG Codearchiv & Module

Neue Antwort erstellen

 

bizzl

Betreff: Mein Formelparser aus BPS #20

BeitragDo, Mai 31, 2012 22:41
Antworten mit Zitat
Benutzer-Profile anzeigen
Ich habe den Formelparser aus meinem Beitrag zu BPS #20 (Kopfrechnen-Spiel)
https://www.blitzforum.de/forum...hp?t=38666
weiterentwickelt:
BlitzMax: [AUSKLAPPEN]

Rem
Dies ist die erweiterte Version des Formelparsers aus meinem Beitrag zu BPS #20
Dieser neue Formelparser behersscht nun auch Klammerrechnung.

Ich plane eine Erweiterung auf trigonometrische Funktionen (sin,cos u.s.w.)
endrem


SuperStrict

Type TOperator
Field operator:String '+ - * / ^ ...
Field pri:Int 'dessen Priorität
EndType

Global TermListe:TList = New TList

'Die Funktion "Rechnen" ist die Startfunktion des Formelparsers.
'Hier wird die als String übergebene Rechenaufgabe in eine Liste (TermListe) umgewandelt.
'In dieser Liste stehen abwechselnd die Zahlen und die Operatoren mit ihrer Priorität.
'Wenn eine öffnende Klammer "(" gefunden wird, wird die Prioritöt erhöht.
'Wenn eine schließende Klammer ")" gefunden wird, wird die Prioritöt vermindert.
'Durch diesen Trick ist eine (fast) unbegrentzte Klammertiefe möglich.
Function Rechnen:String(Ausdruck:String)
Local term:String , pri:Int = 0 'Priorität bei Klammern
Termliste.clear()
'Ausdruck normalisieren:
Ausdruck = Ausdruck.Replace("," , ".") 'Kommata durch Dezimalpunkte ersetzen
Ausdruck = Ausdruck.Replace(" " , "") 'Leerzeichen löschen
Ausdruck = Ausdruck.tolower() 'kleinSchreibung
For Local position:Int = 0 Until Ausdruck.length
Local zeichen:Byte = Ausdruck[position]
If zeichen = Asc("(") pri:+ 100 ; Continue 'Klammer gefunden?...
If zeichen = Asc(")") pri:- 100 ; Continue '... ja,dann Priorität anpassen.
If zeichen = Asc("-") And term = "" term = "-" ; Continue 'negative Zahl
If zeichen >= Asc("0") And zeichen <= Asc("9") Or zeichen=Asc(".") 'Ziffer oder Dezimalpunkt
term:+ Chr(zeichen)
Else 'Rechenzeichen gefunden?
TermListe.addlast(term)
term = ""
Local t:TOperator = New TOperator
t.operator = Chr(zeichen)
t.pri = 0
Select Chr(zeichen)
Case "+" , "-" t.pri = 10 + pri
Case "*" , "/" t.pri = 11 + pri
Case "^" t.pri = 12 + pri

EndSelect
If t.pri > 0 'Gültiger Operator?
TermListe.addlast(t) 'dann an Liste anhängen
Else
Notify("Unbekannter Operator ~q" + t.operator + "~q") ; End 'Syntax Error
EndIf
EndIf
Next
TermListe.addlast(term) 'Letzten Operanden anhängen
Prioritaet() 'Liste nach Priorität abarbeiten
Return String(Termliste.first()) 'und Ergebnis zurückgeben
EndFunction

'In der Funktion "Prioritaet" wird die Termliste solange nach dem Operator mit der jeweils
'höchsten Priorität durchsucht, bis in der Termliste nur noch ein Element steht,
'diese Element ist das Endergebnis.
'Der gefundene Ausdruck wird in der Funktion "BerechneAusdruck" berechnet.
Function Prioritaet()
Repeat
Local pri:Int = 0
Local ausdruck:TOperator
For Local c:TOperator = EachIn TermListe
If c.pri > pri
pri = c.pri
ausdruck = c
EndIf
Next 'Der Ausdruck mit der höchsten Priorität wurde gefunden und...
BerechneAusdruck(ausdruck) '... berechnet
Until TermListe.count() = 1
EndFunction

'Der in der Funktion "Priorität" gefundene Ausdruck wird hier berechnet.
'Der Ausdruck und die beiden dazugehörigen Operanden werden aus der Termliste
'gelöscht und durch das Ergebnis der mathematischen Operation ersetzt.
Function BerechneAusdruck(ausdruck:Toperator)
Local operator:TLink = TermListe.findlink(ausdruck) 'Operator finden
Local LinkerOperand:TLink = operator.PrevLink() 'Linken und rechten Operanden...
Local RechterOperand:TLink = operator.NextLink() '...holen
Local links:String = String(LinkerOperand.Value() )
Local rechts:String = String(RechterOperand.Value() )
Local op:TOperator = TOperator(operator.Value() )
Local ergebnis:String
Select op.operator 'Berechnug durchführen
Case "^" ergebnis = Double(links) ^ Double(rechts)
Case "*" ergebnis = Double(links) * Double(rechts)
Case "/"
If Double(rechts) = 0 Notify("Division durch Null!") ; End
ergebnis = Double(links) / Double(rechts)
Case "+" ergebnis = Double(links) + Double(rechts)
Case "-" ergebnis = Double(links) - Double(rechts)
EndSelect
operator.remove() 'Den berechneten Ausdruck...
LinkerOperand.remove()
TermListe.InsertAfterLink(ergebnis , RechterOperand) '...durch das Ergebnis...
RechterOperand.remove() '... ersetzen
EndFunction

Print Rechnen("((12 5-50) *4)^2")
'sollte 90000 rauskommmen,
'125 - 50 = 75
' 75 * 4 = 300
'300 ^ 2 = 90000




Ich weiss, meine gross- Kleinschreibung... Sad

ToeB

BeitragFr, Jun 01, 2012 0:39
Antworten mit Zitat
Benutzer-Profile anzeigen
Sehr schön gelöst das Problem Smile

Obwohl ich glaube das eine Rekursive Lösung von Vorteil wäre (von der Klammer her). Ich wollte auch mal son Ding schreiben, war aber irgendwie immer zu Faul es dann auch wirklich zu tun aber hast echt gute arbeit gemacht !

Lg
Religiöse Kriege sind Streitigkeiten erwachsener Männer darum, wer den besten imaginären Freund hat.
Race-Project - Das Rennspiel der etwas anderen Art
SimpleUDP3.0 - Neuste Version der Netzwerk-Bibliothek
Vielen Dank an dieser Stelle nochmal an Pummelie, welcher mir einen Teil seines VServers für das Betreiben meines Masterservers zur verfügung stellt!
 

bizzl

BeitragFr, Jun 01, 2012 7:23
Antworten mit Zitat
Benutzer-Profile anzeigen
Danke,ToeB!

Ich habe vor einer kleinen Ewigkeit so ein Programm in C++ auf dem Amiga geschrieben, rekursiv und mit
überladenen Operatoren.

Die Aufgabe in BPS #20 war ein willkommener Anlass, nochmal einen Formelparser zu schreiben.
Rekursion wäre tatsächlich eine Option gewesen, aber das war mir dann doch (im Moment) zu viel Aufwand.

BladeRunner

Moderator

BeitragFr, Jun 01, 2012 8:23
Antworten mit Zitat
Benutzer-Profile anzeigen
Schöner Code. Habe mir erlaubt dass Ganze in einen Type auszulagern und im Falle reiner Integer die Ausgabe auf Long umzustellen.
BlitzMax: [AUSKLAPPEN]
Rem
Dies ist die erweiterte Version des Formelparsers aus meinem Beitrag zu BPS #20
Dieser neue Formelparser behersscht nun auch Klammerrechnung.

Ich plane eine Erweiterung auf trigonometrische Funktionen (sin,cos u.s.w.)
endrem


SuperStrict

Type TOperator
Field operator:String '+ - * / ^ ...
Field pri:Int 'dessen Priorität
EndType
Type TMathParser

Global TermListe:TList = New TList

'Die Funktion "Rechnen" ist die Startfunktion des Formelparsers.
'Hier wird die als String übergebene Rechenaufgabe in eine Liste (TermListe) umgewandelt.
'In dieser Liste stehen abwechselnd die Zahlen und die Operatoren mit ihrer Priorität.
'Wenn eine öffnende Klammer "(" gefunden wird, wird die Prioritöt erhöht.
'Wenn eine schließende Klammer ")" gefunden wird, wird die Prioritöt vermindert.
'Durch diesen Trick ist eine (fast) unbegrentzte Klammertiefe möglich.
Method Rechnen:String(Ausdruck:String)
Local term:String , pri:Int = 0 'Priorität bei Klammern
Termliste.clear()
'Ausdruck normalisieren:
Ausdruck = Ausdruck.Replace("," , ".") 'Kommata durch Dezimalpunkte ersetzen
Ausdruck = Ausdruck.Replace(" " , "") 'Leerzeichen löschen
Ausdruck = Ausdruck.tolower() 'kleinSchreibung
For Local position:Int = 0 Until Ausdruck.length
Local zeichen:Byte = Ausdruck[position]
If zeichen = Asc("(") pri:+ 100 ; Continue 'Klammer gefunden?...
If zeichen = Asc(")") pri:- 100 ; Continue '... ja,dann Priorität anpassen.
If zeichen = Asc("-") And term = "" term = "-" ; Continue 'negative Zahl
If zeichen >= Asc("0") And zeichen <= Asc("9") Or zeichen=Asc(".") 'Ziffer oder Dezimalpunkt
term:+ Chr(zeichen)
Else 'Rechenzeichen gefunden?
TermListe.addlast(term)
term = ""
Local t:TOperator = New TOperator
t.operator = Chr(zeichen)
t.pri = 0
Select Chr(zeichen)
Case "+" , "-" t.pri = 10 + pri
Case "*" , "/" t.pri = 11 + pri
Case "^" t.pri = 12 + pri

EndSelect
If t.pri > 0 'Gültiger Operator?
TermListe.addlast(t) 'dann an Liste anhängen
Else
Notify("Unbekannter Operator ~q" + t.operator + "~q") ; End 'Syntax Error
EndIf
EndIf
Next
TermListe.addlast(term) 'Letzten Operanden anhängen
Prioritaet() 'Liste nach Priorität abarbeiten
Return String(Termliste.first()) 'und Ergebnis zurückgeben
EndMethod

'In der Funktion "Prioritaet" wird die Termliste solange nach dem Operator mit der jeweils
'höchsten Priorität durchsucht, bis in der Termliste nur noch ein Element steht,
'diese Element ist das Endergebnis.
'Der gefundene Ausdruck wird in der Funktion "BerechneAusdruck" berechnet.
Method Prioritaet()
Repeat
Local pri:Int = 0
Local ausdruck:TOperator
For Local c:TOperator = EachIn TermListe
If c.pri > pri
pri = c.pri
ausdruck = c
EndIf
Next 'Der Ausdruck mit der höchsten Priorität wurde gefunden und...
BerechneAusdruck(ausdruck) '... berechnet
Until TermListe.count() = 1
EndMethod

'Der in der Funktion "Priorität" gefundene Ausdruck wird hier berechnet.
'Der Ausdruck und die beiden dazugehörigen Operanden werden aus der Termliste
'gelöscht und durch das Ergebnis der mathematischen Operation ersetzt.
Method BerechneAusdruck(ausdruck:Toperator)
Local operator:TLink = TermListe.findlink(ausdruck) 'Operator finden
Local LinkerOperand:TLink = operator.PrevLink() 'Linken und rechten Operanden...
Local RechterOperand:TLink = operator.NextLink() '...holen
Local links:String = String(LinkerOperand.Value() )
Local rechts:String = String(RechterOperand.Value() )
Local op:TOperator = TOperator(operator.Value() )
Local ergebnis:String
If (links.contains(".") = 0) And (rechts.contains(".") = 0) Then
Select op.operator 'Berechnug durchführen
Case "^" ergebnis = Long(Long(links) ^ Long(rechts))
Case "*" ergebnis = Long(Long(links) * Long(rechts))
Case "/"
If Long(rechts) = 0 Notify("Division durch Null!") ; End
ergebnis = Long(Long(links) / Long(rechts))
Case "+" ergebnis = Long(Long(links) + Long(rechts))
Case "-" ergebnis = Long(Long(links) - Long(rechts))
EndSelect
Else
Select op.operator 'Berechnug durchführen
Case "^" ergebnis = Double(links) ^ Double(rechts)
Case "*" ergebnis = Double(links) * Double(rechts)
Case "/"
If Double(rechts) = 0 Notify("Division durch Null!") ; End
ergebnis = Double(links) / Double(rechts)
Case "+" ergebnis = Double(links) + Double(rechts)
Case "-" ergebnis = Double(links) - Double(rechts)
EndSelect
EndIf
operator.remove() 'Den berechneten Ausdruck...
LinkerOperand.remove()
TermListe.InsertAfterLink(ergebnis , RechterOperand) '...durch das Ergebnis...
RechterOperand.remove() '... ersetzen
EndMethod
EndType

Print New TMathParser.Rechnen("((12 5-50) *4)^2")
'sollte 90000 rauskommmen,
'125 - 50 = 75
' 75 * 4 = 300
'300 ^ 2 = 90000

Zu Diensten, Bürger.
Intel T2300, 2.5GB DDR 533, Mobility Radeon X1600 Win XP Home SP3
Intel T8400, 4GB DDR3, Nvidia GF9700M GTS Win 7/64
B3D BMax MaxGUI

Stolzer Gewinner des BAC#48, #52 & #92

Neue Antwort erstellen


Übersicht BlitzMax, BlitzMax NG Codearchiv & Module

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group