Autor Beitrag
Michael15
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 76

Win XP Professional
Delphi 2, 3, 7, 8
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic star
Beiträge: 720

Win 98, Win ME, Win2k, Win XP
D3 Pro
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 76

Win XP Professional
Delphi 2, 3, 7, 8
BeitragVerfasst: Sa 19.02.05 23:12 
Gibt's da auch ne andere Lösung???
WeBsPaCe
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Beiträge: 2322
Erhaltene Danke: 1

FireFox 3, Internet Explorer 6 SP1
D1, D3Prof, D6Pers, D7Pers+Indy, VisualStudio Express
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic star
Beiträge: 720

Win 98, Win ME, Win2k, Win XP
D3 Pro
BeitragVerfasst: Sa 19.02.05 23:22 
das ist dann schon der bereich vom
Parser
Michael15 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 76

Win XP Professional
Delphi 2, 3, 7, 8
BeitragVerfasst: Sa 19.02.05 23:23 
was ist das???
WeBsPaCe
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Beiträge: 2322
Erhaltene Danke: 1

FireFox 3, Internet Explorer 6 SP1
D1, D3Prof, D6Pers, D7Pers+Indy, VisualStudio Express
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic star
Beiträge: 720

Win 98, Win ME, Win2k, Win XP
D3 Pro
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 76

Win XP Professional
Delphi 2, 3, 7, 8
BeitragVerfasst: Sa 19.02.05 23:26 
Kann mir jemand den Quelltext schicken???
Kroni
ontopic starontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic star
Beiträge: 720

Win 98, Win ME, Win2k, Win XP
D3 Pro
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 76

Win XP Professional
Delphi 2, 3, 7, 8
BeitragVerfasst: Sa 19.02.05 23:30 
soll ich den Quelltext mal schreiben:
Hier Bitte

ausblenden volle Höhe Delphi-Quelltext
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;

(****************************************)
(********* Eigene Prozeduren ************)
(****************************************)

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;


(****************************************)
(********* Delphi Prozeduren ************)
(****************************************)

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 user profile iconraziel: Delphi-Tags hinzugefügt.
Kroni
ontopic starontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic star
Beiträge: 720

Win 98, Win ME, Win2k, Win XP
D3 Pro
BeitragVerfasst: Sa 19.02.05 23:34 
Benutze mal bitte Delphi-Tags!
Mal zu deinem PRogrammierstiel:
Du kannst einfach jeden Zahlbutton die Procedure
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
procedure TForm1.zahlbutton(Sender:TObject)
begin
if sender=Button1 then.....//wenn button 1 geklickt
if sender=Button2 then.....//wenn button 2 geklickt
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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 76

Win XP Professional
Delphi 2, 3, 7, 8
BeitragVerfasst: Sa 19.02.05 23:37 
und wie kann man das problem lösen???
mit meiner Programmiuerweise (mein lehrer will das so, sorry)
delfiphan
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2684
Erhaltene Danke: 32



BeitragVerfasst: 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:

ausblenden 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
ausblenden 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:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
Type
OutputType = record
 Length : Integer; // Länge Substring
 Wert : Extended;  // Auswertung des Substrings
end;


Hier noch einige Definitionen von Operatortypen u.ä.:
ausblenden Delphi-Quelltext
1:
2:
Type
 cTyp = (cSTRICHOP,cPUNKTOP,cKLAMMERAUF,cKLAMMERZU,cUNDEFINIERT,cPUNKT);


Hier sind also die parseXYZ Funktionen foward deklariert:
ausblenden 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.

ausblenden volle Höhe Delphi-Quelltext
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 >= 1then
  case S[start] of
   '.': sTyp := cPUNKT; // also eigentlich Komma ;)
   '+','-': 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 // wir sind gar nicht mehr im String
 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
// NatZahl :- Ziffer NatZahl | Ziffer
 if parseZiffer(start, Ziffer) then
 begin
  inc(start, Ziffer.Length);
  if parseNATZAHL(start, Zahl) then // Fall 1
  begin
   Output.Length := Ziffer.Length + Zahl.Length;
   Output.Wert := Ziffer.Wert*Power(10,Zahl.Length) + Zahl.Wert; // 10er System
   Result := True;
   exit;
  end;
  Output := Ziffer; // Fall 2
  Result := True;
  exit;
 end;
 Result := False;
end;
function parseZAHL(start : Integer; var Output : OutputType) : Boolean;
Var
 NatZahl, NachKomma : OutputType;
 ignore : Char;
begin
// Zahl :- NatZahl "." NatZahl | NatZahl
 if parseNATZAHL(start, NatZahl) then
 begin
  inc(start, NatZahl.Length);
  if parseCHARTYP(start, cPUNKT,ignore) then
  begin
   inc(start);
   if parseNATZAHL(start, NachKomma) then // Fall 1: Nachkommastellen vorhanden
   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; // Fall 2: Ohne Nachkommastellen
  Result := True;
  exit;
 end;
 Result := False;
end;
function parseKLAMMER(start : Integer; var Output : OutputType) : Boolean;
var
 ignore : Char;
 Ausdruck : OutputType;
begin
// Klammer :- "(" Ausdruck ")"
 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
// Ausdruck :- Addition | Term
 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
// Faktor :- Zahl | Klammer
 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
 // Multiplikation :- Faktor PunktOp (Multiplikation | Faktor)
 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
// Term StrichOp (Addition | Term)
 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
 // Multiplikation | Faktor
 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:
ausblenden 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)';    // Var S : String ist global allen Funktionen zugänglich
 if parseSTRING(1, Output) then
 begin
  Str(Output.Wert:0:3, S);
  ShowMessage('Das Resultat lautet:'+S); // 215.6
 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2684
Erhaltene Danke: 32



BeitragVerfasst: 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.

ausblenden volle Höhe Delphi-Quelltext
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
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

// (can be used / modifed without permission by the author. code comes with no warranties whatsoever. credits not required)
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 >= 1then  
 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 // Vorzeichen vorhanden?  
  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 // ohne Vorzeichen  
 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 // Vorzeichen vor Klammer?  
  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  
 // Faktor * Multiplikation | Faktor * Faktor  
 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  
// Term + Addition | Term + Term  
 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  
 // Multiplikation | Faktor  
 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); // Delphi-Auswertung 
  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:
ausblenden 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
ausblenden Quelltext
1:
2:
 2-*3   // = Syntaxfehler
 2(-3)  // = Syntaxfehler