Autor |
Beitrag |
Michael15
Beiträge: 76
Win XP Professional
Delphi 2, 3, 7, 8
|
Verfasst: Sa 19.02.05 23:05
Ein Taschenrechner hat:
Die Ziffertasten 1,2,3,4,5,6,7,8,9 und 0.
Er hat die vier Grundrechenarten. (+,-,*,/)
Er hat ein EditFeld.
Der Taschenrechner rechnet schon, nur wenn man z.b. rechnen will:
5 + 5 + 5, dann streikt er. Wie kann man das Problem beheben, dass er nur zwei Zahlen miteinander +,-,*,/ nimmt. sondern er müsste auch drei oder 4mal rechnen können???
5 + 5 = 10
5 + 5 - 6 * 4 = ???? (wie geht das?????)
Danke
|
|
Kroni
Beiträge: 720
Win 98, Win ME, Win2k, Win XP
D3 Pro
|
Verfasst: Sa 19.02.05 23:11
ich habe das bei meinem so gelöst, dass ich den anwender GEZWUNGEN habe, nach dem er zwei zahlen eingegeben hat, = zu drücken und dann kannste damit ja weiter rechnen!
nachdem er dann = gedrückt hat, MUSS er eine Operatortaste drücken, um weiterrechnen zu können!
Gruß, Kroni
|
|
Michael15
Beiträge: 76
Win XP Professional
Delphi 2, 3, 7, 8
|
Verfasst: Sa 19.02.05 23:12
Gibt's da auch ne andere Lösung???
|
|
WeBsPaCe
Beiträge: 2322
Erhaltene Danke: 1
FireFox 3, Internet Explorer 6 SP1
D1, D3Prof, D6Pers, D7Pers+Indy, VisualStudio Express
|
Verfasst: Sa 19.02.05 23:19
Du kannst ähnlich wie bei Hangman die einzelnen Teile in dem Editfeld bestimmen und dann vom Anfang bis zum nächsten Math.-Operator rechnen und dann weiter. Müsste funktionieren.
_________________ Steht der Bauer im Gemüse, hat er später grüne Füße.
|
|
Kroni
Beiträge: 720
Win 98, Win ME, Win2k, Win XP
D3 Pro
|
Verfasst: Sa 19.02.05 23:22
das ist dann schon der bereich vom
Parser
|
|
Michael15
Beiträge: 76
Win XP Professional
Delphi 2, 3, 7, 8
|
Verfasst: Sa 19.02.05 23:23
|
|
WeBsPaCe
Beiträge: 2322
Erhaltene Danke: 1
FireFox 3, Internet Explorer 6 SP1
D1, D3Prof, D6Pers, D7Pers+Indy, VisualStudio Express
|
Verfasst: Sa 19.02.05 23:24
Michael15 hat folgendes geschrieben: | was ist das??? |
Irgendwas auslesen und damit weiterarbeiten.
_________________ Steht der Bauer im Gemüse, hat er später grüne Füße.
|
|
Kroni
Beiträge: 720
Win 98, Win ME, Win2k, Win XP
D3 Pro
|
Verfasst: Sa 19.02.05 23:25
da gibts du dann
(3+4)*(5-8*36) ein und der geht dann den String durch, und berechnet ihn dir nach Punkt vor Strichrechnung usw...
oder wenn du nen richtigen Parser hast, gibst du
3=5x-8 oder sowas ein und der Parser berechnet dir dann den Wert für x!
|
|
Michael15
Beiträge: 76
Win XP Professional
Delphi 2, 3, 7, 8
|
Verfasst: Sa 19.02.05 23:26
Kann mir jemand den Quelltext schicken???
|
|
Kroni
Beiträge: 720
Win 98, Win ME, Win2k, Win XP
D3 Pro
|
Verfasst: Sa 19.02.05 23:28
von nem Parser??
guck dich mal um, davon gibts schon ein paar codes....
ist nur verdammt viel arbeit!
und wenn das für ne hausaufgabe oder so ist, wird dir dein lehrer never abkaufen, dass du den geschrieben hast!
|
|
Michael15
Beiträge: 76
Win XP Professional
Delphi 2, 3, 7, 8
|
Verfasst: Sa 19.02.05 23:30
soll ich den Quelltext mal schreiben:
Hier Bitte
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229:
| var display : real; flag : string;
procedure stop_klick; begin halt; end;
procedure clear_klick; begin form1.edit1.clear; end;
procedure gleich_klick; begin if flag = 'plus' then begin display := display + strtofloat (form1.Edit1.Text); form1.Edit1.Text := floattostr (display); flag := 'Zackenbarsch'; end; if flag = 'minus' then begin display := display - strtofloat (form1.Edit1.Text); form1.Edit1.Text := floattostr (display); flag := 'Pampelmusenbrei'; end; if flag = 'mal' then begin display := display * strtofloat (form1.Edit1.Text); form1.Edit1.Text := floattostr (display); flag := 'Zwiebelsack'; end; if flag = 'durch' then begin display := display / strtofloat (form1.Edit1.Text); form1.Edit1.Text := floattostr (display); flag := 'Käseauflauf'; end; end;
procedure plus_klick; begin display := strtofloat (form1.Edit1.Text); form1.Edit1.clear; flag := 'plus'; end;
procedure minus_klick; begin display := strtofloat (form1.Edit1.Text); form1.Edit1.clear; flag := 'minus'; end;
procedure mal_klick; begin display := strtofloat (form1.Edit1.Text); form1.Edit1.clear; flag := 'mal'; end;
procedure durch_klick; begin display := strtofloat (form1.Edit1.Text); form1.Edit1.clear; flag := 'durch'; end;
procedure root_klick; begin display := strtofloat (form1.Edit1.Text); form1.Edit1.clear; flag := 'root'; end;
procedure eins_klick; begin form1.edit1.text := form1.edit1.text + '1' end;
procedure zwei_klick; begin form1.edit1.text := form1.edit1.text + '2' end;
procedure drei_klick; begin form1.edit1.text := form1.edit1.text + '3' end;
procedure vier_klick; begin form1.edit1.text := form1.edit1.text + '4' end;
procedure fuenf_klick; begin form1.edit1.text := form1.edit1.text + '5' end;
procedure sechs_klick; begin form1.edit1.text := form1.edit1.text + '6' end;
procedure sieben_klick; begin form1.edit1.text := form1.edit1.text + '7' end;
procedure acht_klick; begin form1.edit1.text := form1.edit1.text + '8' end;
procedure neun_klick; begin form1.edit1.text := form1.edit1.text + '9' end;
procedure null_klick; begin form1.edit1.text := form1.edit1.text + '0' end;
procedure komma_klick; begin form1.edit1.text := form1.edit1.text + ',' end;
procedure TForm1.Button1Click(Sender: TObject); begin stop_klick; end;
procedure TForm1.Button2Click(Sender: TObject); begin gleich_klick; end;
procedure TForm1.Button3Click(Sender: TObject); begin clear_klick; end;
procedure TForm1.Button4Click(Sender: TObject); begin eins_klick; end;
procedure TForm1.Button5Click(Sender: TObject); begin zwei_klick; end;
procedure TForm1.Button6Click(Sender: TObject); begin drei_klick; end;
procedure TForm1.Button7Click(Sender: TObject); begin vier_klick; end;
procedure TForm1.Button8Click(Sender: TObject); begin fuenf_klick; end;
procedure TForm1.Button9Click(Sender: TObject); begin sechs_klick; end;
procedure TForm1.Button10Click(Sender: TObject); begin sieben_klick; end;
procedure TForm1.Button11Click(Sender: TObject); begin acht_klick; end;
procedure TForm1.Button12Click(Sender: TObject); begin neun_klick; end;
procedure TForm1.Button13Click(Sender: TObject); begin null_klick; end;
procedure TForm1.Button14Click(Sender: TObject); begin plus_klick; end;
procedure TForm1.Button15Click(Sender: TObject); begin minus_klick; end;
procedure TForm1.Button16Click(Sender: TObject); begin mal_klick; end;
procedure TForm1.Button17Click(Sender: TObject); begin durch_klick; end;
procedure TForm1.Button18Click(Sender: TObject); begin komma_klick; end; |
Moderiert von raziel: Delphi-Tags hinzugefügt.
|
|
Kroni
Beiträge: 720
Win 98, Win ME, Win2k, Win XP
D3 Pro
|
Verfasst: Sa 19.02.05 23:34
Benutze mal bitte Delphi-Tags!
Mal zu deinem PRogrammierstiel:
Du kannst einfach jeden Zahlbutton die Procedure
Delphi-Quelltext 1: 2: 3: 4: 5:
| procedure TForm1.zahlbutton(Sender:TObject) begin if sender=Button1 then.....if sender=Button2 then.....end; |
schreiben und dann den zahlenbutton die procedure zahlbutton im onclick event im Objectinspektor zuweisen!
ist wesentlich eleganter!
außerden, warum schreibst du nicht direkt in die button1click procedure??warum den Code unnötig aufblähen
|
|
Michael15
Beiträge: 76
Win XP Professional
Delphi 2, 3, 7, 8
|
Verfasst: Sa 19.02.05 23:37
und wie kann man das problem lösen???
mit meiner Programmiuerweise (mein lehrer will das so, sorry)
|
|
delfiphan
Beiträge: 2684
Erhaltene Danke: 32
|
Verfasst: So 20.02.05 03:12
Titel: Parsing
Zitat: |
3=5x-8 oder sowas ein und der Parser berechnet dir dann den Wert für x!
|
Gleichungen auflösen hat eigentlich nicht direkt mit Parsing zu tun.
Du kannst den folgenden Code für deine Hausaufgaben wohl eher nicht gebrauchen.
Wie dem auch sei, ich hab mir vorhin (wegen euch ;)) die Aufgabe gestellt, einen einfachen Parser zu programmieren. Dieser Post soll dazu dienen zu zeigen, wie man sowas anpacken kann. Den Parser kann man natürlich beliebig erweitern. Ich zeige hier einfach mal die Implementation der Grundoperationen.
Der Parser nimmt einen String und wertet ihn aus, falls der Syntax korrekt ist. Die Operatoren +,.,*,/ werden unterstützt; sowie Kommazahlen.
Also z.B. "(5+6*(7+8*9+10))/(1.25+2*3-4.75)" (=215.6)
Vorgehensweise:
Man muss sich als erstes eine formale Grammatik definieren. Man kann das auch ohne formale Grammatik machen; so sollte es aber 1. erweiterbar und 2. übersichtlicher sein und 3. hat man automatisch einen vollständigen Syntaxcheck drin.
Also zunächst muss man sich die "Regeln der Mathematik" (bzw. der Mathematiknotation) ganz genau überlegen und aufschreiben. Die werden dann später genau so implementiert. Ich hoffe echt dass ich kein Überlegungsfehler drin hab:
Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| Ziffer :- "0" | "1" | ... | "9" StrichOp :- "+" | "-" PunktOp :- "*" | "/"
Ausdruck :- Addition | Term Addition :- Term StrichOp (Addition | Term) Term :- Multiplikation | Faktor Multiplikation :- Faktor PunktOp (Multiplikation | Faktor) Faktor :- Zahl | "(" Ausdruck ")" Zahl :- NatZahl "." NatZahl | NatZahl (Zahl ist entweder mit oder ohne Komma) NatZahl :- Ziffer NatZahl | Ziffer (Natürliche Zahlen plus die Null) |
Das ist jetzt nicht genau in EBNF, aber es sollte einigermassen verständlich sein:
":-" heisst so viel wie "falls" (das ist Prolog-Syntax).
"|" bedeutet "oder".
Bevor man anfängt irgendwas zu parsen, sollte man erst mal die Whitespaces aus dem String rauslöschen!
D.h. "1 + 2" ist der falsche "Syntax". Es muss im Format "1+2" sein.
Um nun die Lösung der Rechnung zu erhalten, muss man "Ausdruck" (wie oben definiert) parsen, dann hat man's ;).
"Ausdruck" baut auf "Addition" und "Term" auf. Das geht so weiter, bis man schlussendlich bei "Ziffer", einer Operation oder bei einer Klammer ist (die bestehen dann nur noch aus einem einzigen Zeichen).
Es ist nun relativ einfach, die einzelnen kleinen Blöcke zu programmieren, da sie eben aufeinander aufbauen.
Eine Zahl wie "123.345" wird mit der obigen Grammatik direkt korrekt geparst, es ist kein strtoint() o.ä. nötig. Man kann sich also ziemlich "einfache" Regeln aufstellen, die dann einzeln relativ einfach zu implementieren sind. Man kann damit ziemlich verrückte Dinge machen...
(Was hier mit diesem einfachen Parser nicht möglich ist, sind links-gebundene Operatoren zu definieren. Alle Operatoren +,-,/,* sind hier rechts-gebunden definiert: "1+2+3" wird also als "1+(2+3)" interpretiert. Der Hoch-Operator lässt sich beispielsweise nicht implementieren: Er ist nicht assotiativ: (2^3)^4 ist ungleich 2^(3^4). Er wird von Taschenrechnern oft links-gebunden interpretiert: 2^3^4 bedeutet (2^3)^4. Negative Zahlen werden hier auch nicht unterstützt: Ein "(-1)" wird nicht interpretiert. Eine Verallgemeinerung des Parsers wäre natürlich aber möglich)
Das Programm ist nicht optimiert aber ich hoffe es dient denjenigen, die sowas noch nie gesehen haben. Ist möglich, dass der Code nicht fehlerfrei ist, hab nur einige Ausdrücke probiert, und die wurden korrekt ausgewertet.
Also, zur Erklärung: Die Funktionen
Delphi-Quelltext 1:
| function parseXYZ(start : Integer; var Output : OutputType) : Boolean; |
versuchen "XYZ" ab Position "start" im globalen String "S" auszuwerten.
Falls erfolgreich, gibt die Funktion true zurück und speichert die Auswertung des Termes und die Länge des Substrings in "Output", sonst gibt die Funktion false zurück.
"OutputType" ist so definiert:
Delphi-Quelltext 1: 2: 3: 4: 5:
| Type OutputType = record Length : Integer; Wert : Extended; end; |
Hier noch einige Definitionen von Operatortypen u.ä.:
Delphi-Quelltext 1: 2:
| Type cTyp = (cSTRICHOP,cPUNKTOP,cKLAMMERAUF,cKLAMMERZU,cUNDEFINIERT,cPUNKT); |
Hier sind also die parseXYZ Funktionen foward deklariert:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| function parseNATZAHL(start : Integer; var Output : OutputType) : Boolean; forward; function parseKLAMMER(start : Integer; var Output : OutputType) : Boolean; forward; function parseZIFFER(start : Integer; var Output : OutputType) : Boolean; forward; function parseZAHL(start : Integer; var Output : OutputType) : Boolean; forward; function parseAUSDRUCK(start : Integer; var Output : OutputType) : Boolean; forward; function parseFAKTOR(start : Integer; var Output : OutputType) : Boolean; forward; function parseMULTIPLIKATION(start : Integer; var Output : OutputType) : Boolean; forward; function parseADDITION(start : Integer; var Output : OutputType) : Boolean; forward; function parseTERM(start : Integer; var Output : OutputType) : Boolean; forward; |
Folgend sind die Prozeduren "parseXYZ" programmiert. Sie haben alle das gleichen Muster (ausser parseCHARTYP und parseZIFFER, welche speziell programmiert sind).
Die Funktionen implementieren einfach die Regeln, die ich oben angegeben hab.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194:
| function parseCHARTYP(start : Integer; Typ : cTyp; var Operation : Char) : Boolean; Var sTyp : cTyp; begin if (start <= length(S)) and (start >= 1) then case S[start] of '.': sTyp := cPUNKT; '+','-': sTyp := cSTRICHOP; '*','/': sTyp := cPUNKTOP; '(','[','{': sTyp := cKLAMMERAUF; ')',']','}': sTyp := cKLAMMERZU else sTyp := cUNDEFINIERT; end; if sTyp = Typ then begin Operation := S[start]; Result := True; exit; end; Result := False; end; function parseZIFFER(start : Integer; var Output : OutputType) : Boolean; begin if not ((start <= length(S)) and (start >= 1)) then begin result := False; exit; end; Case S[start] of '0'..'9': begin Output.Length := 1; Output.Wert := ord(S[start])-ord('0'); Result := True; exit; end; end; Result := False; end; function parseNATZAHL(start : Integer; var Output : OutputType) : Boolean; Var Ziffer, Zahl : OutputType; begin if parseZiffer(start, Ziffer) then begin inc(start, Ziffer.Length); if parseNATZAHL(start, Zahl) then begin Output.Length := Ziffer.Length + Zahl.Length; Output.Wert := Ziffer.Wert*Power(10,Zahl.Length) + Zahl.Wert; Result := True; exit; end; Output := Ziffer; Result := True; exit; end; Result := False; end; function parseZAHL(start : Integer; var Output : OutputType) : Boolean; Var NatZahl, NachKomma : OutputType; ignore : Char; begin if parseNATZAHL(start, NatZahl) then begin inc(start, NatZahl.Length); if parseCHARTYP(start, cPUNKT,ignore) then begin inc(start); if parseNATZAHL(start, NachKomma) then begin Output.Length := NatZahl.Length + 1 + NachKomma.Length; Output.Wert := NatZahl.Wert + NachKomma.Wert / Power(10, NachKomma.Length); Result := True; exit; end; end; Output := NatZahl; Result := True; exit; end; Result := False; end; function parseKLAMMER(start : Integer; var Output : OutputType) : Boolean; var ignore : Char; Ausdruck : OutputType; begin if parseCHARTYP(start, cKLAMMERAUF, ignore) then begin inc(start); if parseAUSDRUCK(start, Ausdruck) then begin inc(start, Ausdruck.Length); if parseCHARTYP(start, cKLAMMERZU, ignore) then begin Output.Length := 1 + Ausdruck.Length + 1; Output.Wert := Ausdruck.Wert; Result := True; exit; end; end; end; Result := False; end; function parseAUSDRUCK(start : Integer; var Output : OutputType) : Boolean; begin if parseADDITION(start, Output) or parseTERM(start, Output) then begin Result := True; exit; end; Result := False; end; function parseFAKTOR(start : Integer; var Output : OutputType) : Boolean; begin if parseZAHL(start, Output) or parseKLAMMER(start, Output) then begin Result := True; exit; end; Result := False; end; function parseMULTIPLIKATION(start : Integer; var Output : OutputType) : Boolean; var Faktor1, Faktor2 : OutputType; Operation : Char; begin if parseFAKTOR(start, Faktor1) then begin inc(start, Faktor1.Length); if parseCHARTYP(start, cPUNKTOP, Operation) then begin inc(start); if parseMULTIPLIKATION(start, Faktor2) or parseFAKTOR(start, Faktor2) then begin Output.Length := Faktor1.Length + 1 + Faktor2.Length; Case Operation of '*': Output.Wert := Faktor1.Wert * Faktor2.Wert; '/': Output.Wert := Faktor1.Wert / Faktor2.Wert; end; Result := true; exit; end; end; end; Result := False; end; function parseADDITION(start : Integer; var Output : OutputType) : Boolean; var Term1, Term2 : OutputType; Operation : Char; begin if parseTERM(start, Term1) then begin inc(start, Term1.Length); if parseCHARTYP(start, cSTRICHOP, Operation) then begin inc(start); if parseADDITION(start, Term2) or parseTERM(start, Term2) then begin Output.Length := Term1.Length + 1 + Term2.Length; Case Operation of '+': Output.Wert := Term1.Wert + Term2.Wert; '-': Output.Wert := Term1.Wert - Term2.Wert; end; Result := true; exit; end; end; end; Result := False; end; function parseTERM(start : Integer; var Output : OutputType) : Boolean; begin if parseMULTIPLIKATION(start, Output) or parseFAKTOR(start, Output) then begin Result := True; exit; end; Result := False; end; function parseSTRING(start: Integer; var Output : OutputType) : Boolean; begin Result := parseAUSDRUCK(start, Output) and (Output.Length = length(S)); end; |
Testprogramm:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| procedure TForm1.FormCreate(Sender: TObject); Var Output : OutputType; begin S := '(5+6*(7+8*9+10))/(1.25+2*3-4.75)'; if parseSTRING(1, Output) then begin Str(Output.Wert:0:3, S); ShowMessage('Das Resultat lautet:'+S); end else ShowMessage('Syntaxfehler.'); end; |
Gruss,
Simon
// Edit:
- Compilerswitch {$B-} unbedingt nötig (ist default)
- uses Math nicht vergessen
Übrigens: Um nochmals zu
Zitat: | 3=5x-8 oder sowas ein und der Parser berechnet dir dann den Wert für x! |
zurückzukommen: Den Wert x (numerisch) auszurechnen ist eigentlich keine Hexerei. Man setzt auf beiden Seiten des Gleichheitszeichens Klammern und ersetzt das Gleichheitszeichen durch ein Minus. Danach sieht das ganze so aus: "(3)-(5x-8)". Jetzt geht es nur noch drum die Nullstellen finden. Numerisch geht das einfach mit dem Sekantenverfahren (->lässt sich in 3-4 Zeilen programmieren).
Die Grammatik müsste man noch erweitern, sodass "x" zugelassen wäre:
Variable :- "x"
Zahl :- Variable | NatZahl "." NatZahl | NatZahl
Multiplikation :- Faktor PunktOp (Multiplikation | Faktor) | Faktor Variable
Den Ausdruck symbolisch nach x aufzulösen ist dann aber eine andere Geschichte!
Zuletzt bearbeitet von delfiphan am Sa 06.08.05 02:56, insgesamt 2-mal bearbeitet
|
|
delfiphan
Beiträge: 2684
Erhaltene Danke: 32
|
Verfasst: So 20.02.05 13:48
Titel: Mathe-Parser
Hoffentlich findet das alles jemand sinnvoll, denn ich brauch's eigentlich nicht wirklich :\...
Wie auch immer, hier noch die backfertige Funktion evalMath(const S : String) : Extended;
Wertet S aus; gibt NaN zurück, falls ein Syntaxfehler vorhanden ist.
S darf keine Whitespaces enthalten.
Hab den Syntax von oben noch ein wenig erweitert, sodass jetzt auch negative Zahlen wie "(-1)" möglich sind.
Grüsse,
Simon
Edit:
Die Unit "umath" hab ich übrigens hier gepostet:
www.delphi-forum.de/...Variablen_36946.html
Die Unit umath arbeitet auch mit Variablen, negativen Zahlen, Funktionen, etc.. Sie ist auch ein bisschen besser programmiert, ist aber vielleicht schwerer zu verstehen. Die Version, die ich im vorigen Post beschrieben hab arbeitet ohne jegliche Schleifen (kein for, kein while, kein repeat). Die Version in umath verwendet Schleifen und eine etwas grosszügigere "Grammatikbeschreibung", die auch den Hoch-Operator unterstützt.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292: 293: 294: 295: 296: 297:
| {$B-} unit Unit1;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, Math, StdCtrls;
type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private public end;
var Form1: TForm1;
implementation
{$R *.dfm}
Function evalMath(const S : String) : Extended; type OutputType = record Length : Integer; Wert : Extended; end; Type cTyp = (cSTRICHOP,cPUNKTOP,cKLAMMERAUF,cKLAMMERZU,cUNDEFINIERT,cPUNKT); function parseNATZAHL(start : Integer; var Output : OutputType) : Boolean; forward; function parseKLAMMER(start : Integer; var Output : OutputType) : Boolean; forward; function parseZIFFER(start : Integer; var Output : OutputType) : Boolean; forward; function parseZAHL(start : Integer; var Output : OutputType) : Boolean; forward; function parseAUSDRUCK(start : Integer; var Output : OutputType) : Boolean; forward; function parseFAKTOR(start : Integer; var Output : OutputType) : Boolean; forward; function parseMULTIPLIKATION(start : Integer; var Output : OutputType) : Boolean; forward; function parseADDITION(start : Integer; var Output : OutputType) : Boolean; forward; function parseTERM(start : Integer; var Output : OutputType) : Boolean; forward; function parseMITKOMMAZAHL(start : Integer; var Output : OutputType) : Boolean; forward;
function parseCHARTYP(start : Integer; Typ : cTyp; var Operation : Char) : Boolean; Var sTyp : cTyp; begin sTyp := cUNDEFINIERT; if (start <= length(S)) and (start >= 1) then case S[start] of '.': sTyp := cPUNKT; '+','-': sTyp := cSTRICHOP; '*','/': sTyp := cPUNKTOP; '(': sTyp := cKLAMMERAUF; ')': sTyp := cKLAMMERZU; end; if sTyp = Typ then begin Operation := S[start]; Result := True; exit; end; Result := False; end; function parseZIFFER(start : Integer; var Output : OutputType) : Boolean; begin if not ((start <= length(S)) and (start >= 1)) then begin result := False; exit; end; Case S[start] of '0'..'9': begin Output.Length := 1; Output.Wert := ord(S[start])-ord('0'); Result := True; exit; end; end; Result := False; end; function parseNATZAHL(start : Integer; var Output : OutputType) : Boolean; Var Ziffer, Zahl : OutputType; begin if parseZiffer(start, Ziffer) then begin inc(start, Ziffer.Length); if parseNATZAHL(start, Zahl) then begin Output.Length := Ziffer.Length + Zahl.Length; Output.Wert := Ziffer.Wert*Power(10,Zahl.Length) + Zahl.Wert; Result := True; exit; end; Output := Ziffer; Result := True; exit; end; Result := False; end; function parseZAHL(start : Integer; var Output : OutputType) : Boolean; var Operation : Char; Zahl : OutputType; begin if parseCHARTYP(start, cSTRICHOP, Operation) then if parseZAHL(start+1, Zahl) then begin Output.Length := 1 + Zahl.Length; if Operation = '-' then Output.Wert := -Zahl.Wert else Output.Wert := Zahl.Wert; Result := True; exit; end; if parseMITKOMMAZAHL(start, Zahl) then begin Output.Length := Zahl.Length; Output.Wert := Zahl.Wert; Result := True; exit; end; Result := False; end;
function parseMITKOMMAZAHL(start : Integer; var Output : OutputType) : Boolean; Var NatZahl, NachKomma : OutputType; ignore : Char; begin if parseNATZAHL(start, NatZahl) then begin inc(start, NatZahl.Length); if parseCHARTYP(start, cPUNKT,ignore) then begin inc(start); if parseNATZAHL(start, NachKomma) then begin Output.Length := NatZahl.Length + 1 + NachKomma.Length; Output.Wert := NatZahl.Wert + NachKomma.Wert / Power(10, NachKomma.Length); Result := True; exit; end; end; Output := NatZahl; Result := True; exit; end; Result := False; end; function parseKLAMMER(start : Integer; var Output : OutputType) : Boolean; var Operation, ignore : Char; Ausdruck, Klammer : OutputType; begin if parseCHARTYP(start, cSTRICHOP, Operation) then if parseKLAMMER(start+1, Klammer) then begin Output.Length := 1 + Klammer.Length; if Operation = '-' then Output.Wert := -Klammer.Wert else Output.Wert := Klammer.Wert; Result := True; exit; end; if parseCHARTYP(start, cKLAMMERAUF, ignore) then begin inc(start); if parseAUSDRUCK(start, Ausdruck) then begin inc(start, Ausdruck.Length); if parseCHARTYP(start, cKLAMMERZU, ignore) then begin Output.Length := 1 + Ausdruck.Length + 1; Output.Wert := Ausdruck.Wert; Result := True; exit; end; end; end; Result := False; end; function parseAUSDRUCK(start : Integer; var Output : OutputType) : Boolean; begin if parseADDITION(start, Output) or parseTERM(start, Output) then begin Result := True; exit; end; Result := False; end; function parseFAKTOR(start : Integer; var Output : OutputType) : Boolean; begin if parseZAHL(start, Output) or parseKLAMMER(start, Output) then begin Result := True; exit; end; Result := False; end; function parseMULTIPLIKATION(start : Integer; var Output : OutputType) : Boolean; var Faktor1, Faktor2 : OutputType; Operation : Char; begin if parseFAKTOR(start, Faktor1) then begin inc(start, Faktor1.Length); if parseCHARTYP(start, cPUNKTOP, Operation) then begin inc(start); if parseMULTIPLIKATION(start, Faktor2) or parseFAKTOR(start, Faktor2) then begin Output.Length := Faktor1.Length + 1 + Faktor2.Length; Case Operation of '*': Output.Wert := Faktor1.Wert * Faktor2.Wert; '/': Output.Wert := Faktor1.Wert / Faktor2.Wert; end; Result := true; exit; end; end; end; Result := False; end; function parseADDITION(start : Integer; var Output : OutputType) : Boolean; var Term1, Term2 : OutputType; Operation : Char; begin if parseTERM(start, Term1) then begin inc(start, Term1.Length); if parseCHARTYP(start, cSTRICHOP, Operation) then begin inc(start); if parseADDITION(start, Term2) or parseTERM(start, Term2) then begin Output.Length := Term1.Length + 1 + Term2.Length; Case Operation of '+': Output.Wert := Term1.Wert + Term2.Wert; '-': Output.Wert := Term1.Wert - Term2.Wert; end; Result := true; exit; end; end; end; Result := False; end; function parseTERM(start : Integer; var Output : OutputType) : Boolean; begin if parseMULTIPLIKATION(start, Output) or parseFAKTOR(start, Output) then begin Result := True; exit; end; Result := False; end; Var Output : OutputType; begin if parseAUSDRUCK(1, Output) and (Output.Length = length(S)) then Result := Output.Wert else Result := NaN; end;
procedure TForm1.Button1Click(Sender: TObject); Var E1, E2 : Extended; S : String; begin S := '(5+6*(-7+8*9+10))/(1.25+2*3-4.75)+(---3)+-1--(123)'; E1 := evalMath(S); if not isNan(E1) then begin E2 := (5+6*(-7+8*9+10))/(1.25+2*3-4.75)+(---3)+-1--(123); ShowMessage(Format('geparster String: "%s"'#13+ 'evalMath(): %f'#13+ 'Delphi: %f',[S,E1,E2])); end else ShowMessage('Syntaxfehler'); end;
end. |
Edit
PS: Die Ausdrücke werden von evalMath gleich ausgewertet wie vom Delphi-Compiler:
Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| -2*3 // = -6 -(2*3) // = -6 2*(-3) // = -6 2*-(3) // = -6 2*-3 // = -6 --2*-3 // = -6 ---2*3 // = -6 +-2*+3 // = -6 -(-(-6)) // = -6 ---6 // = -6 |
Fehlerhafte Ausdrücke
Quelltext 1: 2:
| 2-*3 // = Syntaxfehler 2(-3) // = Syntaxfehler |
|
|
|