Autor |
Beitrag |
wayne123
      
Beiträge: 52
|
Verfasst: Sa 19.11.11 17:41
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:
| unit Unit1;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type TForm1 = class(TForm) Edit1: TEdit; Button1: TButton; Memo1: TMemo; procedure FormCreate(Sender: TObject); procedure Button1Click(Sender: TObject); procedure Multi(Zahl1,Zahl2:integer; var Term:string); procedure Add(Zahl1,Zahl2:integer; var Term:string); procedure Sub(Zahl1,Zahl2:integer; var Term:string); procedure Divi(Zahl1,Zahl2:integer; var Term:string); procedure Rechnen(Eingabe:string); private public end;
var Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject); begin memo1.text := ''; end;
procedure TForm1.Button1Click(Sender: TObject); begin Memo1.Text:=''; Rechnen(Edit1.Text); end;
procedure Tform1.Rechnen(Eingabe:string); var Eingabe2,Klammer,Klammer2:string; PosKlammerauf,PosKlammerzu,i,zahl1,zahl2:integer; begin if pos('(',Eingabe)>0 then begin repeat begin posklammerzu := 0; Posklammerauf:=0; i:=0; repeat begin if eingabe[i] ='(' then begin posklammerauf := i ; end; if eingabe[i] =')' then begin posklammerzu := i ; end; i:=i+1; end until (posklammerzu <> 0) or (i=length(eingabe)+1); Klammer:= Copy(Eingabe,Posklammerauf,posklammerzu-posklammerauf+1); Klammer2:=Klammer; repeat if ((pos('/',Klammer)> pos('*',Klammer)) or (pos('/',Klammer)=0)) and (pos('*',Klammer)<>0) then Multi(Zahl1,Zahl2,Klammer) else if pos('/',Klammer)<>0 then Divi(Zahl1,Zahl2,Klammer); until (pos('*',Klammer)=0) and (pos('/',Klammer)=0); repeat if ((pos('-',Klammer)> pos('+',Klammer)) or (pos('-',Klammer)=0)) and (pos('+',Klammer)<>0) then Add(Zahl1,Zahl2,Klammer) else if pos('-',Klammer)<>0 then Sub(Zahl1,Zahl2,Klammer); until (pos('+',Klammer)=0) and (pos('-',Klammer)=0); Eingabe:=Stringreplace(Eingabe,Klammer2,copy(Klammer,2,length(Klammer)-2),[rfreplaceall]); end; until pos('(',Eingabe)=0; end; Eingabe2:=Eingabe; repeat if ((pos('/',Eingabe)> pos('*',Eingabe)) or (pos('/',Eingabe)=0)) and (pos('*',Eingabe)<>0) then Multi(Zahl1,Zahl2,Eingabe) else if pos('/',Eingabe)<>0 then Divi(Zahl1,Zahl2,Eingabe); until (pos('*',Eingabe)=0) and (pos('/',Eingabe)=0); repeat if ((pos('-',Eingabe)> pos('+',Eingabe)) or (pos('-',Eingabe)=0)) and (pos('+',Eingabe)<>0) then Add(Zahl1,Zahl2,Eingabe) else if pos('-',Eingabe)<>0 then Sub(Zahl1,Zahl2,Eingabe); until (pos('+',Eingabe)=0) and (pos('-',Eingabe)=0); Eingabe:=Stringreplace(Eingabe,Eingabe2,copy(Eingabe,2,length(Eingabe)-2),[rfreplaceall]); Memo1.Text:=Memo1.Text + Eingabe end;
procedure Tform1.Multi(zahl1,zahl2:integer ;var Term:string); var Ersatz:extended; begin zahl1:= pos('*',Term) ; repeat begin zahl1:= zahl1-1 ; end until (Term[zahl1] = '(') or (Term[zahl1] = '+') or (Term[zahl1] = '-') or (Term[zahl1] = '*') or (Term[zahl1] = '/') or (Term[zahl1] = ''); zahl2:= pos('*',Term) ; repeat begin zahl2:= zahl2+1 ; end until (Term[zahl2] = ')') or (Term[zahl2] = '+') or (Term[zahl2] = '-') or (Term[zahl2] = '*') or (Term[zahl2] = '/') or (Term[zahl2] = ''); Ersatz:=StrToFloat(Copy(Term,Zahl1+1,pos('*',Term)-Zahl1-1))* StrToFloat(Copy(Term,pos('*',Term)+1,Zahl2-pos('*',Term)-1)); Term:=Stringreplace(Term,Copy(Term,Zahl1+1,Zahl2-Zahl1-1),FloatToStr(Ersatz),[rfreplaceall]) end;
procedure Tform1.Add(zahl1,zahl2:integer ;var Term:string); var Ersatz:extended; begin zahl1:= pos('+',Term) ; repeat begin zahl1:=zahl1-1; end until (Term[zahl1] = '(') or (Term[zahl1] = '+') or (Term[zahl1] = '-') or (Term[zahl1] = '*') or (Term[zahl1] = '/') or (Term[zahl1] = ''); zahl2:= pos('+',Term) ; repeat begin zahl2:= zahl2+1 ; end until (Term[zahl2] = ')') or (Term[zahl2] = '+') or (Term[zahl2] = '-') or (Term[zahl2] = '*') or (Term[zahl2] = '/') or (Term[zahl2] = ''); Ersatz:=StrToFloat(Copy(Term,Zahl1+1,pos('+',Term)-Zahl1-1))+ StrToFloat(Copy(Term,pos('+',Term)+1,Zahl2-pos('+',Term)-1)); Term:=Stringreplace(Term,Copy(Term,Zahl1+1,Zahl2-Zahl1-1),FloatToStr(Ersatz),[rfreplaceall]) end;
procedure Tform1.Sub(Zahl1,Zahl2:integer; var Term:string); var Ersatz:extended; begin zahl1:= pos('-',Term) ; repeat begin zahl1:= zahl1-1 ; end until (Term[zahl1] = '(') or (Term[zahl1] = '+') or (Term[zahl1] = '-') or (Term[zahl1] = '*') or (Term[zahl1] = '/') or (Term[zahl1] = ''); zahl2:= pos('-',Term) ; repeat begin zahl2:= zahl2+1 ; end until (Term[zahl2] = ')') or (Term[zahl2] = '+') or (Term[zahl2] = '-') or (Term[zahl2] = '*') or (Term[zahl2] = '/') or (Term[zahl2] = ''); Ersatz:=StrToFloat(Copy(Term,Zahl1+1,pos('-',Term)-Zahl1-1))- StrToFloat(Copy(Term,pos('-',Term)+1,Zahl2-pos('-',Term)-1)); Term:=Stringreplace(Term,Copy(Term,Zahl1+1,Zahl2-Zahl1-1),FloatToStr(Ersatz),[rfreplaceall]) end;
procedure Tform1.Divi(Zahl1,Zahl2:integer; var Term:string); var Ersatz:extended; begin zahl1:= pos('/',Term) ; repeat begin zahl1:= zahl1-1 ; end until (Term[zahl1] = '(') or (Term[zahl1] = '+') or (Term[zahl1] = '-') or (Term[zahl1] = '*') or (Term[zahl1] = '/') or (Term[zahl1] = ''); zahl2:= pos('/',Term) ; repeat begin zahl2:= zahl2+1 ; end until (Term[zahl2] = ')') or (Term[zahl2] = '+') or (Term[zahl2] = '-') or (Term[zahl2] = '*') or (Term[zahl2] = '/') or (Term[zahl2] = ''); Ersatz:=StrToFloat(Copy(Term,Zahl1+1,pos('/',Term)-Zahl1-1))/ StrToFloat(Copy(Term,pos('/',Term)+1,Zahl2-pos('/',Term)-1)); Term:=Stringreplace(Term,Copy(Term,Zahl1+1,Zahl2-Zahl1-1),FloatToStr(Ersatz),[rfreplaceall]) end; end. |
Weiß jemand, wie ich das machen, dass man negative Zahlen eingeben kann oder auch negative Zahlen rauskommen? Da ich mit - immer sofort rechne und er das Minus bei der Zahl nicht mitumwandeln kann in integer, weiß ich nicht, wie ich das in dem Programm hinbekommen kann und neu anfangen möcht ich auch nicht. Gibts da noch irgendeine Möglichkeit?
|
|
GuaAck
      
Beiträge: 378
Erhaltene Danke: 32
Windows 8.1
Delphi 10.4 Comm. Edition
|
Verfasst: Sa 19.11.11 20:44
Hallo,
die Regel ist doch einfach:
Vorangegangenes Zeichen: (Zeilenanfang), *, /, (: Das "-" kann nur Vorzeichen der folgenden Zahl sein,
Vorangegangenes Zeichen: Zahl,): Das minus ist der Operator, der die vorangegangene Operation abschließt und das Ergebnis mit dem folgenden Ausdruck per Subtraktion verbindet.
Offen ist, was bei z. B. "3--2" passieren soll (wäre ja 5).
ABER: Dein Code enthält an verschiedenen Stellen die Analyse der Syntax für die Subtraktion. Meine Empfehlung: Fange neu an und nutze Schnipsel aus Deinem bisherigen Code. Als Ansatz:
PROCEDURE Bearbeite_Ausdruck(Ausdruck: String);
BEGIN
IF Linke_Klammer_da dann
___suche zugehörige Rechte_Klammer
___Bearbeite_Ausdruck(Ausdruck_zwischen_den_Klammern)
ELSE
.... jetzt hier wie in Deinem Code
(Nur grobe Skizze, nach der ersten linken Klammer muss man dann nach der nächsten linken Klammer nach dem Ausdruck suchen usw. )
Manche Leute haben Bedenken bei rekursivem (sich selbst aufrufendem) Code, aber je Ausdruck ein eigenes Objekt zu kreieren macht auch nichts Anderes.
Viel Erfolg,
Gruß GuaAck
|
|
Martok
      
Beiträge: 3661
Erhaltene Danke: 604
Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
|
Verfasst: Sa 19.11.11 21:22
Genau das Problem hatte ich mit meinem Taschenrechner auch. Da ist allerdings ein Tokenizer davor, daher ging folgendes:
Ich überprüfe, ob an der Stelle ein - als Subtraktion Sinn macht (es also eine linke Seite gibt). Wenn dem nicht so ist, muss es eine Negation sein. Du könntest also prüfen, ob links von dem - etwas anderes eine Ziffer/Zahl steht. Ist das so, ists eine Subtraktion, sonst eine Negation.
Muss allerdings zugeben, dass ich deinen Code grade nicht überblicke und daher keine Ahnung hab ob du das verwenden kannst 
_________________ "The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
|
|
wayne123 
      
Beiträge: 52
|
Verfasst: So 20.11.11 01:19
GuaAck hat folgendes geschrieben : | (Nur grobe Skizze, nach der ersten linken Klammer muss man dann nach der nächsten linken Klammer nach dem Ausdruck suchen usw. ) |
Dass ich die Klammern zuerst raussuche und damit rechne, tu ich ja schon. Er guckt erst, ob Klammern da sind und rechnet die auch in der richtigen Reihenfolge aus, dann rechnet er mit dem gesamten Term. Könnte ich das alles nicht umgehen, indem ih am Anfang gucke, welche - negative Zahlen angeben und diese dann im Programm ändere, später in den Rechnungen aber als - ansehe und wenn irgendwo etwas negatives rauskommt, wieder das - in etwas anderes umwandeln? Auch wenns etwas umständlich wär und auch nicht schön gemacht wär, würde das irgendwie so funktionieren und wenn ja, wie ansatzweise? Wie man da was rekursives reinmachen könnte, wär für mich jetzt ein Rätsel, da ich rekursive Funktionen jetzt auch erst einen Monat kenne und insgesamt halt gerade mal ein Jahr Delphi kenne.
Martok hat folgendes geschrieben : | Ich überprüfe, ob an der Stelle ein - als Subtraktion Sinn macht (es also eine linke Seite gibt). Wenn dem nicht so ist, muss es eine Negation sein. Du könntest also prüfen, ob links von dem - etwas anderes eine Ziffer/Zahl steht. Ist das so, ists eine Subtraktion, sonst eine Negation. |
Dann müsste es auch gehen, die minuszeichen, die negative Zahlen anzeigen, in etwas anderes umzuwandeln, bis man damit rechnet und bei Ergebnissen, die negativ sind, dann auch das Minuszeichen umzuwandeln, oder? Also zb. das - in _ umwandeln und dann immer das ergebnis *-1 rechnen, wenn ein _ davor steht.
Moderiert von Narses: Komplett-Zitate gekürzt.
|
|
bummi
      
Beiträge: 1248
Erhaltene Danke: 187
XP - Server 2008R2
D2 - Delphi XE
|
Verfasst: So 20.11.11 01:45
irgendwo im Web findet man parser9 free im Source.
Ich habe mir das seinerzeit durchgelesen und meine dass das dortige Vorgehen optimal sein dürfte ...
_________________ Das Problem liegt üblicherweise zwischen den Ohren H₂♂
DRY DRY KISS
|
|
Martok
      
Beiträge: 3661
Erhaltene Danke: 604
Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
|
Verfasst: So 20.11.11 02:03
wayne123 hat folgendes geschrieben : | Dann müsste es auch gehen, die minuszeichen, die negative Zahlen anzeigen, in etwas anderes umzuwandeln, bis man damit rechnet und bei Ergebnissen, die negativ sind, dann auch das Minuszeichen umzuwandeln, oder? Also zb. das - in _ umwandeln und dann immer das ergebnis *-1 rechnen, wenn ein _ davor steht. |
Tja - und wie findest du raus, ob das - ein Vorzeichen oder eine Subtraktion ist? Eben, du musst das sowieso alles parsen.
bummi hat folgendes geschrieben : | irgendwo im Web findet man parser9 free im Source.
Ich habe mir das seinerzeit durchgelesen und meine dass das dortige Vorgehen optimal sein dürfte ... |
Naja, optimal bei weitem nicht. Er ist ganz ordentlich, aber weder schön noch klein.
Ansonsten kannst du auch mal den tyParser/Compiler angucken. Der ist beides, aber etwas schwer zu lesen. Aber wenn man erstmal durchsteigt sehr elegant (und verdammt schnell).
Und in dem Beitrag ist auch ein einfacher Parser drin, da findet man den Teil mit den Vorzeichen auch gleich 
_________________ "The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
|
|
wayne123 
      
Beiträge: 52
|
Verfasst: So 20.11.11 14:40
Ich hab mir jetzt viel mit Parser zwar angeguckt, aber verstehen tu ich das irgendwie garnicht. Braucht man dafür jetzt noch ein anderes Nebenprogramm neben Delphi? Wenn man zugeordnet hat, was Zahl, was Operator usw. ist wie rechnet man damit? Was für Befehle/Begriffe gibts für das überhaupt? Beim ersten Blick auf so ein Programm mit Parser versteh ich erstmal garnichts, weil das komplett neu für mich ist und auch komplett anders aussieht, als das, was ich bisher mit Delphi gemacht habe. Also funktioniert das Parsen in etwa so, dass man zuerst jedem Zeichen eine Bedeutung zuordnet, also, was Zahlen, was Operatoren sind und dann wird mit denen irgendwie gerechnet?
|
|
wayne123 
      
Beiträge: 52
|
Verfasst: So 20.11.11 20:36
Also ich hab jetzt ein Taschenrechner mit Parser hinbekommen, auch wenn ich bei vielen Teilen einiges nach- bzw. abgucken musste. Er rechnet zwar mit negativen Zahlen richtig, nur, wenn man direkt bei der Eingabe negative Zahlen eingibt, klappts immer noch nicht. Kann mir jemand auf den ersten Blick sagen, ob man das in das Programm ohne zu große Umstände reinbekommen muss oder ob ich das nochmal komplett von vorne anfangen muss?
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:
| unit UnitParserTR;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type TForm1 = class(TForm) Button1: TButton; Edit1: TEdit; Memo1: TMemo; procedure FormCreate(Sender: TObject); procedure Button1Click(Sender: TObject); function Rechnen(s:string):extended; function pos0(c:char;s:string):integer; function anfang(s:string;c:char):string; function copyab(const s:string; const i:integer):string; function ende(s:string; c:char):string; private public end;
var Form1: TForm1;
implementation
{$R *.dfm}
function TForm1.Rechnen(s:string): extended; begin if pos0('+',s)>0 then result:=Rechnen(anfang(s,'+'))+Rechnen(ende(s,'+')) else if pos0('-',s)>0 then result:=Rechnen(anfang(s,'-'))-Rechnen(ende(s,'-')) else if pos0('*',s)>0 then result:=Rechnen(anfang(s,'*'))*Rechnen(ende(s,'*')) else if pos0('/',s)>0 then result:=Rechnen(anfang(s,'/'))/Rechnen(ende(s,'/')) else if (s>'') and (s[1]='(') then begin s:=copy(s,2,length(s)-2); result:=Rechnen(s) end else result:=StrToFloat(s); end;
function TForm1.pos0(c:char;s:string):integer; var k,z:integer; begin z:=0; for k:=length(s) downto 1 do begin if s[k]='(' then inc(z); if s[k]=')' then dec(z); if (z=0) and (s[k]=c) then begin result:=k; exit; end; end; result:=0; end;
function TForm1.anfang(s:string;c:char):string; begin anfang:=copy(s,1,pos0(c,s)-1); end;
function TForm1.copyab(const s:string; const i:integer):string; begin result:=copy(s,i,length(s)-i+1) end;
function TForm1.ende(s:string; c:char):string; begin ende:=copyab(s,pos0(c,s)+1) end;
procedure TForm1.FormCreate(Sender: TObject); begin Memo1.Clear; end;
procedure TForm1.Button1Click(Sender: TObject); var term:string; begin term:=edit1.Text; Memo1.Text:=FloatToStr(Rechnen(Term)); end;
end. |
|
|
Blup
      
Beiträge: 174
Erhaltene Danke: 43
|
Verfasst: Mi 23.11.11 10:58
Die Operatoren "*" und "/" (auch das Paar "+" und "-") haben bei deinem Parser nicht den selben Rang.
Für die Formel auf der linken Seite, rechts die jetzige Auflösung:
Delphi-Quelltext 1: 2: 3: 4:
| a + b - c + d = (a + (b - c)) + d a - b + c - d = (a - b) + (c - d) a * b / c * d = (a * (b / c)) * d a / b * c / d = (a / b) * (c / d) |
Für das Vorzeichenproblem müsste die Pos0-Funktion erweitert werden.
Vorzeichen sind zu überspringen.
Im Gegensatz zu einem Operator steht vor einem Vorzeichen:
- kein Zeichen
- "("
- ein Operator
Dabei Leerzeichen natürlich überspringen.
In der Funktion "Rechnen" muss vor der Prüfung auf Klammern noch ein eventuell vorhandenes Vorzeichen entfernt werden:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21:
| if pos0('/',s)>0 then result:=Rechnen(anfang(s,'/'))/Rechnen(ende(s,'/')) else begin s := Trim(s); if pos('+', s) = 1 then begin s := copy(s, 2, length(s) - 1); result := Rechnen(s); end else if pos('-', s) = 1 then begin s := copy(s, 2, length(s) - 1); result := -Rechnen(s); end else if pos('(', s) = 1 then begin s := copy(s, 2, length(s) - 2); result:=Rechnen(s) end else result := StrToFloat(s); end; |
Für diesen Beitrag haben gedankt: wayne123
|
|
wayne123 
      
Beiträge: 52
|
Verfasst: Mi 23.11.11 17:39
@Blup Danke! Funktioniert
|
|
|