Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Term (Zeichenfolge 1+2*3) in Fließkommazahl (https://www.delphipraxis.net/140346-term-zeichenfolge-1-2%2A3-fliesskommazahl.html)

Delphi-Narr 16. Sep 2009 12:57


Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Hallo, ich mache gerade für die Schule einen Taschenrechner und habe ihn auch vom optischen her fertig.
Eingabebuttons für Zahlen, Zeichen und ein Rechenfeld.

Wenn man jetzt 1+2*3 eingibt, was ja 7 ergibt, soll eine String Variable rTerm auf ('1+2*3') gesetzt werden. Soweit kein Problem.
Jetzt kommt der Button "Gleich" ins Spiel. Er soll die Variable rErgebnis : real auf das Ergebnis des Terms setzen, also erst ausrechnen, dann StrToFloat. Hier ist das Problem. Dann will ich das Ergebnis noch ausgeben, das krieg ich aber auch hin.

Delphi-Quellcode:
procedure TForm1.ButtonGleichClick(Sender: TObject);
begin
     rZahl:=StrToFloat(rTerm);
     Edit1.text:=FloatToStr(rZahl);
end;
:coder2:

So klappts ja nicht, habs nur mal probiert. Da Delphi aber Punkt vor Strich rechnet, wollte ich das auf diese Art machen.
Bitte helft mit,
Liebe Grüße!

Forlan 16. Sep 2009 13:09

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Tjaa...
Da musst du dir wohl einen Parser basteln, der dir deinen String zerlegt. So einfach wird das nicht funktionieren.

Uwe Raabe 16. Sep 2009 13:12

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Was dir hier noch fehlt, ist ein Formelinterpreter und der geht über die Aufgabe "Taschenrechner" deutlich hinaus. Ich empfehle dir einen anderen Ansatz, der die Berechnungen gleich beim Tastendruck ausführt - so macht es der Taschenrechner nämlich auch. Übrigens bekommt der bei der Eingabe "1+2*3=" eine 9 heraus, während ein Formelinterpreter eine 7 auswerfen würde.

hugo1990 16. Sep 2009 13:13

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Du kannst mit StrToFloat keinen Term in eine Gleitkommazahl umwandeln. Diese Funktion ist ausschließlich dafür da, eine Kommazahl, welche als Zeichenkette gespeichert ist, in eine Gleitkommazahl umzuwandeln. Für das was du vorhast, benötigst du einen Parser, der den String analysiert und dann die Berechnung durchführt, aber sowas zu programmieren ist alles andere als trivial.
Du solltest dir eher in einer Liste oder einer anderen dafür geeigneten Struktur, die Zahlen und Operatoren merken und diese dann im nachhinein mit den Rechenregeln zusammen rechnen.

Forlan 16. Sep 2009 13:15

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Zitat:

Zitat von Uwe Raabe
Was dir hier noch fehlt, ist ein Formelinterpreter und der geht über die Aufgabe "Taschenrechner" deutlich hinaus. Ich empfehle dir einen anderen Ansatz, der die Berechnungen gleich beim Tastendruck ausführt - so macht es der Taschenrechner nämlich auch. Übrigens bekommt der bei der Eingabe "1+2*3=" eine 9 heraus, während ein Formelinterpreter eine 7 auswerfen würde.

Daher ist ein Parser/Formelinterpreter viel zu komplex für einen "Anfänger". Grundwissen der OOP sind dafür wohl vorausgesetzt, wenn man die Aufgabe denn "sauber" lösen möchte...

Delphi-Narr 16. Sep 2009 13:28

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Danke für die vielen schnellen Antworten. :thumb:

Der "Taschenrechner" (Ergebnis=9) ist Pflicht, es gibt halt Bonuspunkte für den Rechner, der den String erst ausrechnet...
In meiner Gruppe wollen wir halt einen Termberechner machen, der also auch Klammern etc. berechnet.
Es wäre ja auch Möglich, das Ganze mit den Variablen Zahl1 (=1) dann dem Zeichen1 (=+ -> Als 1 gespeichert -> if Zeichen1=1 then +...) und dann wieder Zeichen usw. Dafür müssten nur genug Variablen vorhanden sein. Gibt es die Möglichkeit, diese Variablen dann automatisch je nach Bedarf erstellen zu lassen? Ich starte mit rZahl1, rZeichen1 und rZahl2.
Wenn dann noch was eingegeben wird, soll rZeichen2 automatisch erstellt werden.

Dann beim Gleich Button:

Zahlen sortieren und Zeichen sortieren, nach Name und dann immer abwechselnd anordnen. Zahl,Zeichen,Zahl usw.

Das Ergebnis wird dann so berechnet...

Auch möglich???

hugo1990 16. Sep 2009 13:40

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Wie gesagt, wäre dafür eine Liste sehr praktisch. Allerdings wird für eine einigermaßen gute Implementierung schon ein gewisses Grundverständnis der OOP vorrausgesetzt und ich vermute mal, dass dies bei dir nicht der Fall ist. Und dir jetzt hier einen Code vorzuwerfen, der das erledigt möchte ich auch nicht, da es sich ja um eine Schulaufgabe handelt und du etwas lernen sollst.
Es gäbe noch die Möglichkeit, dass du das ganze über eine TStringList versuchst, was dann doch etwas einfacher für dich sein sollte, aber auch keine tolle Lösung ist. Schau dich einfach mal im Forum um, wie man eine TStringList verwendet und falls du nicht weiter weißt, frag einfach nochmal.

himitsu 16. Sep 2009 13:44

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
jupp, um einen Parser kommst du da nicht drumrum

Zitat:

Daher ist ein Parser/Formelinterpreter viel zu komplex für einen "Anfänger". Grundwissen der OOP sind dafür wohl vorausgesetzt, wenn man die Aufgabe denn "sauber" lösen möchte...
wenn man es nicht all zu Komplex macht unsd sich nur ein einfache Operaionen wie * / + und - beschränkt und keine klammern zuläßt, dann wäre es recht leicht/einfach möglich

z.B.
erstmal alles in seine einzelteile zerlegen und in ein Array damit
Code:
1
+
2
*
3
und dann nach den gängigen mathematischen Regeln nacheinander versuchen dieses aufzuösen

hier z.B. zuerst * und dann +

also erstmal ein "*" suchen, wo davor und danach eine Zahl vorkommt
dieses nun ausrechnen und ersetzen
Code:
1
+
6
nun das Gleiche mit dem "+"

praktisch immer zuerst * und / versuchen aufzulösen,
dann + und -
und zuletzt + mit nur einer Zahl dahinter


immer von vorne nach hinten entsprechende Gruppen (Zahl-Operator-Zahl) suchen
und wenn etwas gefunden wurde, dieses ausrechnen und ersetzen, dann erneut von vorne ganz vorner im Array anfangen das nächste zu finden
und das solange, bis nichts mehr zum Auflösen gefunden wurde

* String in Array zerlegen > je Zahlen und Operatoren pro Eintrag
* wiederhole:
* nach "zahl*zahl" suchen
* - dieses ausrechnen und ersetzen und weiter bei "wiederhole"
* nach "zahl/zahl" suchen
* - dieses ausrechnen und ersetzen und weiter bei "wiederhole"
* nach "zahl+zahl" suchen
* - dieses ausrechnen und ersetzen und weiter bei "wiederhole"
* nach "zahl-zahl" suchen
* - dieses ausrechnen und ersetzen und weiter bei "wiederhole"
* nach "+zahl" suchen
* - dieses zusammensetzen und ersetzen und weiter bei "wiederhole"
* nach "-zahl" suchen
* - dieses zusammensetzen und ersetzen und weiter bei "wiederhole"
* Array wieder zum Strng zusammensetzen und ausgeben
* - also einfach alles aneinanderhängen

mit viel Glück ist dann am Ende nur noch ein Eintrag mit einer Zahl übrig
und wenn nicht, dann paßte etwas in der "Formel" nicht ganz

[edit]
jupp, statt dem Array macht sich wohl eine StringListe hier icht schlecht, da man dort leichter Einträge entfernen kann.
[/edit]

PS: http://www.delphipraxis.net/internal...t.php?t=159592
siehe Demo5 und Demo6
auch wenn es dir wohl nnicht viel Helfen wird, aber immerhin ist der QuellCode ja offen und vielleicht findet du in dem Parser ja ein/zwei Anregungen.

macht mein Parser nicht viel anders - nur daß ich da noch Klammern, Funktionen und Anderes mit drin hab ... nja und halt mit anderen Zahlenstrukturen rechne

Delphi-Narr 16. Sep 2009 13:57

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Sieht gut aus, danke.
Delphi berechnet ja theoretisch:

rZahl:=1+2*3

Dann ist

rZahl=7

Darum müsste man dem doch nur klar machen, dass der die Formel, die in rTerm steckt, berechnen soll.

___________________

Wie zerlege ich sowas denn?

himitsu 16. Sep 2009 14:07

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Delphi berechnet nichts.

Im Compiler ist ein Parser, welcher den Quelltext zerlegt und es dann in der EXE sozusagen in der EXE schon richtig zusammengesetzt eingebaut hat.

Da der Parser nur in dem Compiler drin ist, kannst du diesen nicht in der EXE nutzen und mußt dir einen Eigenen bauen (oder einen Fertigen von wo anders verwenden)

Delphi-Narr 16. Sep 2009 14:09

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Oh...

Dann muss ich halt den Term zerlegen und die Zeichen suchen, wie beschrieben - nur wie???

Forlan 16. Sep 2009 14:10

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Zitat:

Zitat von Delphi-Narr
Oh...

Dann muss ich halt den Term zerlegen und die Zeichen suchen, wie beschrieben - nur wie???

Das hat Himitsu doch bereits geschrieben und imho auch gut erklärt :roll:

freak4fun 16. Sep 2009 14:13

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Zitat:

Zitat von Uwe Raabe
Übrigens bekommt der bei der Eingabe "1+2*3=" eine 9 heraus, während ein Formelinterpreter eine 7 auswerfen würde.

Nicht unter Win7. ;)

hugo1990 16. Sep 2009 14:20

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Zitat:

Zitat von freak4fun
Zitat:

Zitat von Uwe Raabe
Übrigens bekommt der bei der Eingabe "1+2*3=" eine 9 heraus, während ein Formelinterpreter eine 7 auswerfen würde.

Nicht unter Win7. ;)

Der Taschenrechner unter XP sollte auch eine 7 ausspucken, man muss ihn nur auf Wissensschaftlich stellen.


Zitat:

Zitat von Forlan
Zitat:

Zitat von Delphi-Narr
Oh...

Dann muss ich halt den Term zerlegen und die Zeichen suchen, wie beschrieben - nur wie???

Das hat Himitsu doch bereits geschrieben und imho auch gut erklärt :roll:

Ich schätze mal er weiß nicht, wie er in dem Term nach den Zeichen suchen soll. Hierzu wäre zu sagen, dass man auf einzelne Zeichen in einem String folgendermaßen zugreifen kann:

Delphi-Quellcode:
S[1] // greift auf das erste Zeichen im String S zu
Weiterhin wären vielleicht noch die Befehle Copy und Pos hierfür interessant.

himitsu 16. Sep 2009 14:27

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Zitat:

Zitat von freak4fun
Nicht unter Win7. ;)

in XP (SP3) auch nicht

wo kommt denn die andere Antwort plötzlich her?

OK, hab zufällig ihn auch immer auf Wissenschaftlich stehen :angel2:

Delphi-Narr 16. Sep 2009 14:29

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Danke, ich habe noch nicht sooo viel Ahnung. Also wenn ich jetzt rTerm[1] und der Term ist 1+2*3 dann würde der die 1 wählen???
Dann muss ich noch wissen, was ist, wenn im Term steht, 12+3 Der soll ja dann auf zwei Zeichen zugreifen...

hugo1990 16. Sep 2009 14:39

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Ich hoffe, dass du schon mal was von Schleifen gehört hast, besonders von for-Schleifen, denn diese wirst du hier benötigen. Du läufst also mit Hilfe der for-Schleife durch den String durch und prüfst, ob das aktuelle Zeichen ein Operator ist. Wenn dies der Fall ist, dann fügst du die Zeichen zwischen diesem Operator und dem letzten Operator bzw. dem Start, hierfür solltest du dir die Positon in einer extra Variablen merken, in deine StringListe ein, hierfür ist der Befehl Copy sehr nützlich. Außerdem musst du noch den Operator in die Stringliste einfügen, das geht ja recht einfach, da du die Position davon sowieso grad weißt. Jetzt noch die Position des aktuellen Operators merken und dann geht das ganze wieder von vorn los.

himitsu 16. Sep 2009 16:42

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Liste der Anhänge anzeigen (Anzahl: 3)
Das Parsen ist recht einfach ... wie es schon genannt wurde, kann man einfach von vorn nach hinten Zeichen für Zeichen durchgehn und sie entsprechend ihrem Typ (Zahl oder Operator) auf ein Array oder eine StringListe aufteilen.
Ein etwas effektiverer Weg mittels Copy steckt in dem ebenfalls schon verlinkten MatheParser, aber die Geschwindigkeit der Einzelzeichenbehandlung ist für diesen Zweck hier auch vollkommend ausreichend.

Delphi-Quellcode:
Function ParseAndCalc(Const S: String): String;
  Var SL: TStringList;
    i:   Integer;

  Begin
    SL := TStringList.Create;
    Try
      Try
        SL.Add('');
        For i := 1 to Length(S) do
          Case S[i] of
            '0'..'9', ',', '.': Begin
              SL[SL.Count - 1] := SL[SL.Count - 1] + S[i];
            End;
            '*', '/', '+', '-': Begin
              If SL[SL.Count - 1] = '' Then Begin
                // eine leere Zeile kommt vor, wenn mehrere Operatoren
                // hintereinander liegen, z.B. bei '1*+2' bzw. '1 * +2'
                SL.Delete(SL.Count - 1);
              End;
              SL.Add(S[i]);
              SL.Add('');
            End;
            ' ': ; // ignoriere Leerzeichen
            Else Raise Exception.CreateFmt('Ungültiges Zeichen "%s" gefunden.', [S[i]]);
          End;

        // hier alles in SL ausrechnen

      Finally
        Result := '';
        For i := 0 to SL.Count - 1 do Result := Result + SL[i];
      End;
    Finally
      SL.Free;
    End;
  End;

Procedure TForm1.Button1Click(Sender: TObject);
  Begin
    Edit2.Text := ParseAndCalc(Edit1.Text);
  End;
Die ZIP enthält die EXE und den wie hier gekürzten QuellCode.
In der passwortgeschützen 7-Zip steckt der komplette Code.
Das Paswort gebe ich gern an nicht Schüler und andere Interessenten raus, wo ich das Gefühl hab, daß sie es nicht als Hausaufgabenlösung mißbrauchen.

PS: Es sind da auch nur 64 zusätzliche Zeilen mit je 7-8 Zeilen pro Operator (+ - * / und den Vorzeichen + -).
und zum Ausprobieren kennt die Demo also diese Operatoren und den Dezimalpunkt (deutsches Windows = "," und englisches Windows = "."), so wie es halt von StrToFloat behandelt wird.

Wie es ausgerechnet hab ich oben erklärt und es wird einfach nur nacheinander je Operator in einer For-Schleife geschaut ob der Operator vorhanden und berechenbar ist und dann wird wie oben beschrieben vorgegangen.

himitsu 18. Okt 2009 22:46

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
bevor ich's noch vergesse ... das höchst geheime Paßwort ist
Zitat:

das geheime Passwort

Delphi-Narr 19. Okt 2009 11:25

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Danke! Also von Schleifen hab ich gehört, klar...
Ich bau das dann mal ein...

:bounce1:



Edit:

Delphi-Quellcode:
procedure TForm1.ButtonGleichClick(Sender: TObject);
begin
     Ausgabefeld.text:=ParseandCalc(Rechenfeld.text);
end;

//Hier der Parser

Function TForm1.ParseAndCalc(Const S: String): String;
  Var SL: TStringList;
    i:   Integer;

  Begin
    SL := TStringList.Create;
    Try
      Try
        SL.Add('');
        For i := 1 to Length(S) do
          Case S[i] of
            '0'..'9', ',', '.': Begin
              SL[SL.Count - 1] := SL[SL.Count - 1] + S[i];
            End;
            '*', '/', '+', '-': Begin
              If SL[SL.Count - 1] = '' Then Begin
                // eine leere Zeile kommt vor, wenn mehrere Operatoren
                // hintereinander liegen, z.B. bei '1*+2' bzw. '1 * +2'
                SL.Delete(SL.Count - 1);
              End;
              SL.Add(S[i]);
              SL.Add('');
            End;
            ' ': ; // ignoriere Leerzeichen
            Else Raise Exception.CreateFmt('Ungültiges Zeichen "%s" gefunden.', [S[i]]);
          End;

        // hier alles in SL ausrechnen

      Finally
        Result := '';
        For i := 0 to SL.Count - 1 do Result := Result + SL[i];
      End;
    Finally
      SL.Free;
    End;
  End;

Aber wenn ich dann jetzt bspw. 124+2 eingebe steht im Ausgabefeld ebenfalls 124+2 und nicht 126

Könnte das an

Delphi-Quellcode:
Uses StrUtils;
liegen? Ich dachte das wäre bei Delphi 5 sysUtils...

Miolin 19. Okt 2009 14:31

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Ich habe gerade auf Delphi-Treff gesehen, dass dort ein Funktionsinterpreter für einen Taschenrechner in Form eines Tutorials gebaut wird... Vielleicht hilft das ja noch ein bisschen weiter -> hier

Blup 19. Okt 2009 14:32

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Nein, das liegt daran, daß du die letzen beiden Beiträge nicht richtig gelesen hast.
Der Quellcode ist unvollständig und muss von dir an dieser Stelle ergänzt werden:

// hier alles in SL ausrechnen

Wenn du tatsächlich verstehst, was in dem kopierten Quellcode passiert, dürfte das kein Problem sein.

Delphi-Narr 19. Okt 2009 15:07

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Ich hatte mir diese Lösung runtergeladen, da fehlte das auch... Konnte das nur wegen StrUtils nicht kompilieren...

Also so ganz richtig versteh ich das nicht. :(

Sherlock 19. Okt 2009 15:33

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Na, dann musst Du Dir doch die Frage stellen, was SL (oder meinetwegen auch ein Element daraus) eigentlich ist.
Wenn Du die Antwort dazu hast kanns weiter gehen.

Sherlock

himitsu 19. Okt 2009 15:34

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Zitat:

Zitat von Blup
// hier alles in SL ausrechnen

jupp, der Code da zerlegt nur den String und setzt ihn am Ende wieder zusammen

Delphi-Quellcode:
For i := 0 to SL.Count - 3 do
  If TryStrToFloat(SL[i], a) and (SL[i + 1] = '*')
      and TryStrToFloat(SL[i + 2], b) Then Begin
    SL.Delete(i);
    SL.Delete(i);
    SL[i] := FloatToStr(a * b);
    Goto Loop;
  End;
in dem kompletten Code steht dann z.B. sowas drin

dieses schaut, ob irgendwo in SL eine Kombination von "Zahl * Zahl" vorkommt
Delphi-Quellcode:
If   TryStrToFloat(SL[i], a)
  and (SL[i + 1] = '*')
  and TryStrToFloat(SL[i + 2], b) Then Begin
wenn soetwas gefunden wurde, dann wird dieses ausgerechnet und in SL durch das Ergebnis ersetzt
Delphi-Quellcode:
SL.Delete(i + 2);
SL.Delete(i + 1);
SL.Delete(i);
SL.Insert(i, FloatToStr(a * b));
es geht auch andersrum
Delphi-Quellcode:
SL.Insert(i, FloatToStr(a * b));
SL.Delete(i + 1);
SL.Delete(i + 2);
SL.Delete(i + 3);

// bzw.
SL.Insert(i + 3, FloatToStr(a * b));
SL.Delete(i + 2);
SL.Delete(i + 1);
SL.Delete(i);
etwas gekürzter Austausch
Delphi-Quellcode:
SL.Delete(i);
SL.Delete(i);
SL.Delete(i);
SL.Insert(i, FloatToStr(a * b));
und noch mehr gekürzt
Delphi-Quellcode:
SL.Delete(i);
SL.Delete(i);
SL[i] := FloatToStr(a * b);
nja, jedenfalls müssen irgendwie aus der Liste die entsprechenden (hierfür 3) Einträge raus
und dafür das Ergebnis rein

Zitat:

Zitat von Delphi-Narr
Ich hatte mir diese Lösung runtergeladen, da fehlte das auch...

hab grad nochmal nachgesehn ... in Taschenrechner_komplett.7z ist alles drin

Zitat:

Zitat von Delphi-Narr
Konnte das nur wegen StrUtils nicht kompilieren...

Bei mir läuft sogar schon in Delphi 7

Wegen was bezüglich der StrUtils konnte es nicht kompiliert werden? (eine "richtige" Fehlermeldung)

Und welche Delphi-Version hattest du nochmal?
gut, das mit dem Delphi 5 hat sich ja grade geklärt.

ja, dann mußt du einfach diese Unit entfernen und dann schauen bei welchen Funktionen das Delphi nun meckert, weil es diese nicht kennt .... dann könnte man nachsehn ob passende Funktionen irgendwo in D5 existieren oder man sorgt irgendwie für Ersatz. (Ersatzfunktion basteln oder das Problem anders lösen)

[add]
ohne die StrUtils könnte man statt
Delphi-Quellcode:
AnsiMatchText(SL[i - 1], ['*', '/', '+', '-'])
einfach dieses machen
Delphi-Quellcode:
((SL[i - 1] = '*') or (SL[i - 1] = '/') or (SL[i - 1] = '+') or (SL[i - 1] = '-'))

Delphi-Narr 19. Okt 2009 16:15

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Danke für die Antwort. Ich habs auch kompiliert gekriegt, nachdem ich etwas geändert habe...
Ich les mir das und die 7zip mal genau durch...

alzaimar 19. Okt 2009 18:59

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Zitat:

Zitat von himitsu
[add]
ohne die StrUtils könnte man statt
Delphi-Quellcode:
AnsiMatchText(SL[i - 1], ['*', '/', '+', '-'])
einfach dieses machen
Delphi-Quellcode:
((SL[i - 1] = '*') or (SL[i - 1] = '/') or (SL[i - 1] = '+') or (SL[i - 1] = '-'))

Oder
Delphi-Quellcode:
SL[i-1] in ['*','/','+','-']
oder
Delphi-Quellcode:
Pos('*/+-',SL[i-1])<>0
:stupid:

Dein Code kann aber keine Klammern, oder?
Ich hab hier was....
[edit]Code entfernt, da er nicht richtig funktioniert[/edit]

[edit]Verbesserten Code am Ende eingefügt[/edit]

himitsu 19. Okt 2009 19:16

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Liste der Anhänge anzeigen (Anzahl: 2)
Zitat:

Zitat von alzaimar
... Oder ... oder ... :stupid:

OK, das geht auch :)
hatte es nur so gelößt, damit man dann auch noch "mod" und Co. einfach nachrüsten kann :angel:
(wie hier unten die Klammern, beim Zerlegen, reingequetscht werden, braucht man nur 'a'..'z', 'A'..'Z' bei den Zahlen reinmachen und hat auch noch Wörter, wie Named-Operators, mit geparst )



Zitat:

2*+2
ja, das + und - wird doppelt ausgewertet
- einmal als Vorzeichen ganz am Anfang, da höchste Priorität
- und nochmal ganz am Ende als Operator, da niedrigste Priorität



nein, der kann noch keine Klammern

das hier sollte "nur" ein kleiner "einfacher" Anfang werden,
nicht so wie da http://www.delphipraxis.net/internal...t.php?t=159592 :zwinker:

man müßte nur noch "(" und ")" beim Zerlegen mit beachten
Code:
For i := 1 to Length(S) do
  Case S[i] of
    ...
    '*', '/', '+', '-'[color=#ff0000], '(', ')'[/color]: Begin
und dann kommt unten als erste "höchstwertige" Auswertung dieses rein
Delphi-Quellcode:
For i := 0 to SL.Count - 3 do
  If (SL[i] = '(') and TryStrToFloat(SL[i + 1], a) and (SL[i + 2] = ')') Then Begin
    SL.Delete(i + 2);
    SL.Delete(i);
    Goto Loop;
  End;


[add]
hab aber wirklich eine Vereinfachung gefunden :stupid:
Delphi-Quellcode:
If ((i = 0) or AnsiMatchText(SL[i - 1], ['*', '/', '+', '-']))
dieses war ja einfach nur für "ist Operator", was doch auch soviel wie "ist keine Zahl" heißt :lol:

also dieses sollte stattdessen auch gehn :stupid:
Delphi-Quellcode:
If ((i = 0) or not TryStrToFloat(SL[i - 1], b))

[add]
hier noch mit Klammern und Div/Mod

und ja natürlich kann man da vieles noch optimieren und zusammenfassen, aber das kann auch jeder selber machen

SirThornberry 19. Okt 2009 20:11

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Ich persönlich finde es ehrlich gesagt quatsch das ganze in einem String zu speichern um diesen dann wieder auseinander zu nehmen. Mein Taschenrechner aus der Schule hat damals alles direkt bei der Eingabe gerechnet. Dabei ist die Regelung auch recht einfach. Eine Zahl wirst erst zum bisherigen Ergebnis hinzugefügt (im Sinne der gewählten Rechenoperation) wenn die nächste Operation gewählt wurde.
Bei: 1+2*3

ist er also wie folgt vorgegangen (hat man im Display gesehen wann er das Zwischenergebnis angezeigt hat)
Eingabe: 1 -> nichts passiert
Eingabe: + -> 1 wird als Zwischenergebnis angezeigt
Eingabe: 2 -> nichts passiert
Eingabe: * -> 2 wird als Zwischenergebnis der Punktrechnung angezeigt
Eingabe: 3 -> nichts passiert
Eingabe: = -> Gesamt Ergebnis wird angezeigt

Wenn man anstelle des "=" eine Strichoperation gewählt hätte würde er das Zwischenergebnis der Punktoperation zum vorherigen Zwischenergebnis dazu zählen und hätte das Gesamtzwischenergebnis angezeigt. Bei einer weiteren Punktoperation hingegen, wäre weiter auf das Zwischenergebnis der verändert wurden.

alzaimar 19. Okt 2009 20:21

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
So ähnlich hab ich das ja auch gemacht. Nur eben tu ich so, also ob der String schon da wäre. Also ich tu na nich so, denn der Strinque ist ja schon da. Ich prozessiere also Zeichen für Zeichen von links nach rechts, mach kein Look-Ahead und auch sonst keine Tricksereien.

Nur gefrickelt ist es halt. Soll eine Stackmaschine darstellen (ops = Operandenstack, numStack = Zahlen).

Ich glaube, man sollte das nicht zu ernst nehmen. Es war ne 30min-Frickelei, weil ich mal schauen wollte, wie viel ich von diesem Mumpitz noch aus meiner Studienzeit wusste. Nichts, hat sich anschließend herausgestellt.

Ich finde es aber eine hübsche Übung, denn es ist nicht trivial.

Poste doch mal deinen Ansatz.

SirThornberry 19. Okt 2009 20:26

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Zitat:

Zitat von alzaimar
Poste doch mal deinen Ansatz.

Meinst du mich? Ich meinte bei mir einen richtigen Taschenrechner zum anfassen. Also nichts selbst programmiertes. Ich fand es eben nur überflüssig erst alles im String zu speichern um danach wieder daraus zu lesen wenn man diesen Schritt auch überspringen kann. In Delphi hab ich zwar auch schon einen Parser geschrieben aber ich glaube für eine Schulaufgabe ist der zu überladen. Und wenn ich Lehrer wäre würde ich meine Zweifel haben das Schüler man eben einen Parser selbst geschrieben haben. In so einem Fall würde ich mir den in allen Einzelheiten erklären lassen um dies zu überprüfen.

Eine Schwierigkeit beim Parsen ist übrigens auch noch so etwas:
+1++2*+3

Dies ist eine gültige Operation (überall einfach nur die Vorzeichen mit angegeben).
So gut wie jeder Taschenrechner hat für das Vorzeichen eine extra Taste. Wenn man die Eingaben also sofort verarbeite, und nicht erst im nachhinein parst, hat man auch keine Probleme mit 2 aufeinanderfolgenden +, - oder sogar +- oder -+ da man dann bereits bei der Eingabe das Vorzeichen der Zahl zuordnet und sich somit die Zusatzarbeit für das korrekte Parsen spart.

himitsu 19. Okt 2009 23:18

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Eine Schwierigkeit beim Parsen ist übrigens auch noch so etwas:
+1++2*+3
OK, bei einer Liveeingabe ist es schon etwas schwerer, aber auch da wäre es mit relativ geringem Aufwand lösbar.

Beim parsen eines ganzen Strings ist es dagegen auch recht einfach ... hab es schießlich auch hier in dem einfachen Code (siehe vorherige Beiträge) mit drinnen :angel:


Noar, wenn du es unbedingt als Liveeingabe haben willst ...

Hier wird übrigens die selbe Rechenfunktion genutzt, welche auch in Beitrag #28 drin ist,
nur mit einem Unterschied:
Delphi-Quellcode:
Finally
  Result := '';
  For i := 0 to SL.Count - 1 do Begin
    If TryStrToFloat(SL[i], a) Then SL[i] := FloatToStr(a);
    Result := Result + SL[i] + ' ';
  End;
  Result := Trim(Result);
Beim Zusammensetzen, werden eventuell noch nicht bearbeitete Zahlen "normalisiert" und es werden Leerzeichen eingefügt.


Ansonsten wird in dem Programm sozusagen einfach nur der eigegebene Befehl/Zeichen hinten an den Text drangehängt und alles gearst.


Achtung, es sind auch Fehleingaben möglich, welche man aber auch praktisch ausnutzen kann (wie mir grad so in den Sinn kam), denn es werden ja alle möglichen Unterteile dennoch so weit wie möglich aufgelöst.

z.B. kann man sowas eingeben "8 / / 8" ... dieses doppelte "//" wird natürlich nicht aufgelößt,
so kann man jetzt etwas berechnen, macht dann einfach "//" ,
der alte Wert bleibt vorne erhalten und man kann hinten weiterrechnen.

z.B. einfach mal dieses eingeben "3 + 5 / / 2 * 4 ="

oder wie wäre es damit ... einfach mal über Strg+C in das Programm reinkopieren
Zitat:

C 3 + 5 / / 2 * 4 / / 12 * 5 + (8 + 88 * 456) * 5 / 3 =
[info]
was mir grad noch auffiehl ist, daß beim letzen Beispiel das z.B. "8 + 88" vor "88 * 456" berechnet wird, da der String direkt im Edit verwaltet und dort schon aufgelöst wurde

er rechnet praktisch so
Zitat:

C 3 + 5 / / 2 * 4 / / (12 * 5 + ((8 + 88) * 456)) * 5 / 3 =
hab aber jetzt nicht die Zeit da jetzt was zu ändern
- entweder intern den die ganze Formel merken und nur das ausgerechnete anzeigen
- oder (was wohl schöner zum Editieren ist) das "+" dort nicht auflösen, da die zugehörige "88" stärker an das "*" gebunden ist.


PS: gibt es eigentlich ein "Standardevent" für Einfügen (Strg+C)?
aktuell sieht es so aus
Delphi-Quellcode:
Procedure TForm1.EditKeyPress(Sender: TObject; Var Key: Char);
  If Key = #22 Then
und bei mir funktioniert es, aber ich weiß nicht, ob es bei allen auch noch so geht.

alzaimar 20. Okt 2009 06:08

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Zitat:

Zitat von himitsu
OK, bei einer Liveeingabe ist es schon etwas schwerer, aber auch da wäre es mit relativ geringem Aufwand lösbar.

Wie würdest Du die Cursortasten bzw. 'Backspace' umsetzen? :mrgreen:

Nee, im Ernst: Mit meiner Schleife geht das eins-fix-drei, denn man muss ja nur die beiden Stacks 'global', d.h. als private Felder in eine Klasse packen und dem Evaluator eine 'ProcessChar'-Methode spendieren, die ja nur aus dem Schleifenrumpf besteht. Et voilá: Fertig ist der Live-Rechn-o-mat.

himitsu 20. Okt 2009 09:02

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
nja, ich hab das ja jetzt erstmal einfach als 'normales' Edit gelöst und da löscht Backspace (in der hochgeladenen Version das letzte Zeichen und alle neuen Zeichen werden hinten angehängt ... und ich meiner internen alktuellen Version wird dieses alles jeweils an der Cursorposition gemacht und dieses (also mitten drinnen was editieren) würde garnicht gehn, wenn man es direkt in den Stack schiebt.

Delphi-Narr 20. Okt 2009 14:08

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Zitat:

Zitat von alzaimar
Zitat:

Zitat von himitsu
[add]
ohne die StrUtils könnte man statt
Delphi-Quellcode:
AnsiMatchText(SL[i - 1], ['*', '/', '+', '-'])
einfach dieses machen
Delphi-Quellcode:
((SL[i - 1] = '*') or (SL[i - 1] = '/') or (SL[i - 1] = '+') or (SL[i - 1] = '-'))

Oder
Delphi-Quellcode:
SL[i-1] in ['*','/','+','-']
oder
Delphi-Quellcode:
Pos('*/+-',SL[i-1])<>0
:stupid:

Dein Code kann aber keine Klammern, oder?
Ich hab hier was, was zusammengefrickelt ist und auch '2*+2' akzeptiert, glaube ich. Dafür kommt es mit Klammern klar und rechnet alles in einem Abwasch aus:
Delphi-Quellcode:
function Eval(term: string): Integer;
const
  isNone = 0;
  isNum = 1;
  isOp = 2;

var

**** DER CODE FUNKTIONIERT NICHT RICHTIG

  numStack: array[0..10] of Integer;
  Oporder: array[char] of Byte;
  SP: Integer;
  Ops: string;
  c: Char;
  code, sign, number: Integer;


begin
  fillchar(numstack, sizeof(numstack), 0);
  fillchar(OpOrder, sizeof(opOrder), 0);
  opOrder['+'] := 1;
  opOrder['-'] := 1;
  opOrder['*'] := 2;
  opOrder['/'] := 2;
  opOrder['('] := 99;

  sign := 1;
  Ops := '(';
  SP := 0;
  number := 0;
  Result := 0;
  term := term + ')';
  code := isNone;
  for c in term do
    case c of
      '0'..'9': begin
          number := 10 * number + ord(c) - 48;
          code := isNum;
        end;
      '+', '-', '*', '/', '(', ')': begin
          if code = isNum then begin
            numStack[SP] := sign * number;
            sign := 1;
            inc(SP);
            code := isNone;
            number := 0;
          end else if code = isOp then
            if c = '-' then begin
              sign := -sign;
              Continue
            end;

          code := isOp;
          if opOrder[c] <= opOrder[Ops[Length(Ops)]] then begin
            while ops[length(ops)] <> '(' do begin
              dec(sp);
              case ops[length(ops)] of
                '+': numstack[sp - 1] := numstack[sp - 1] + numstack[sp];
                '-': numstack[sp - 1] := numstack[sp - 1] - numstack[sp];
                '*': numstack[sp - 1] := numstack[sp - 1] * numstack[sp];
                '/': numstack[sp - 1] := numstack[sp - 1] div numstack[sp];
              end;
              numstack[sp] := 0;
              setlength(ops, length(ops) - 1);
            end;
          end;
          if c = ')' then
            setlength(ops, length(ops) - 1)
          else
            ops := ops + c;
        end;
    end;
  result := Numstack[0];
end;
Ich find's "schön" kompakt. Na ja, 'schön' ist was anderes... :mrgreen:



Hab diesen Code in einen Testrechner eingebaut, der kennt aber
Delphi-Quellcode:
for c in term do
    case c of
      '0'..'9': begin
nicht... Also kennt er schon, ist aber nicht anwendbar...???

[edit=alzaimar] Mfg, alzaimar[/edit]

gammatester 20. Okt 2009 15:09

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Zitat:

Zitat von Delphi-Narr

Hab diesen Code in einen Testrechner eingebaut, der kennt aber
Delphi-Quellcode:
for c in term do
    case c of
      '0'..'9': begin
nicht... Also kennt er schon, ist aber nicht anwendbar...???

Meinst Du for c in term do? Das kannst Du ersetzen durch
Delphi-Quellcode:
for i:=1 to length(term) do begin
  c := term[i];
  case c of
  //...
end;
Aber Vorsicht! Der Code rechnet zwar mit Klammern aber trotzdem nicht richtig: '3*5+4*2/2' ergibt 11 statt 19!

himitsu 20. Okt 2009 15:22

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Zitat:

Zitat von gammatester
Aber Vorsicht! Der Code rechnet zwar mit Klammern aber trotzdem nicht richtig: '3*5+4*2/2' ergibt 11 statt 19!

aus diesem Grund hatte ich bei meinem Code alles getrennt und dort
erst die Vorzeichen +x und -x
dann die Punktrechnungen *, /, DIV und MOD
und erst zum Schluß + und -
berechnen lassen

sonst stimmt die Reihenfolge natürlich nicht.

alzaimar 20. Okt 2009 19:03

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Zitat:

Zitat von gammatester
Aber Vorsicht! Der Code rechnet zwar mit Klammern aber trotzdem nicht richtig: '3*5+4*2/2' ergibt 11 statt 19!

Danke für den Hinweis. Ich nehm den code oben mal raus und prüfe, wieso das so ist.

[edit] Verbesserter Code[/edit]
Delphi-Quellcode:
function Eval(Term: string): Integer;
  function _Eval(term: string; var i: Integer): Integer;
  const
    isNone = 0;
    isNum = 1;
    isOp = 2;

  var
    numStack: array[0..10] of Integer;
    number, code, sign, sp, j: Integer;
    Ops: string;
    c: Char;

    function OpOrder(c: Char): Integer;
    begin
      if c = '$' then
        Result := -1
      else
        Result := (Pos(c, '+-*/') + 1) div 2;
    end;

  begin
    fillchar(numstack, sizeof(numstack), 0);
    sign := 1;
    Ops := '$';
    SP := 0;
    number := 0;
    Result := 0;
    code := isNone;
    while i < length(term) do begin
      inc(i);
      c := term[i];
      case c of
        '0'..'9': begin
            number := 10 * number + ord(c) - 48;
            code := isNum;
          end;
        '(': begin
            number := _Eval(term, i);
            code := isNum;
          end;
        ' ': continue;
        '+', '-', '*', '/', ')':
          begin
            if code = isNum then begin
              numStack[SP] := sign * number;
              sign := 1;
              inc(SP);
              code := isNone;
              number := 0;
            end else if code = isOp then begin
              if c = '-' then
                sign := -sign;
              Continue
            end;
            code := isop;
            if Length(Ops) > 1 then begin
              while opOrder(c) <= OpOrder(Ops[Length(Ops)]) do begin
                dec(sp);
                case Ops[Length(Ops)] of
                  '*': numstack[sp - 1] := numstack[sp - 1] * numstack[sp];
                  '/': numstack[sp - 1] := numstack[sp - 1] div numstack[sp];
                  '+': numstack[sp - 1] := numstack[sp - 1] + numstack[sp];
                  '-': numstack[sp - 1] := numstack[sp - 1] - numstack[sp];
                end;
                numStack[sp] := 0;
                setLength(Ops, length(Ops) - 1);
              end;
            end;
            Ops := Ops + c;
            if c = ')' then begin
              number := NumStack[0];
              break;
            end;

          end;
      end;
    end;
    result := Number;
  end;

var
  i: Integer;

begin
  i := 0;
  Result := _Eval('(' + term + ')', i);
end;

Delphi-Narr 23. Okt 2009 10:06

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Ich habe jetzt versucht, in SL alles auszurechnen...

Sl enthält bspw.

1
+
2
*
3

Ich hab jetzt überlegt, ich wiederhole jetzt etwas, bis SL nur noch eine Zeile hat.
Nur weiß ich nicht, wie ich das anstellen soll... Ich habs mit Repeat Until versucht:


Delphi-Quellcode:
begin
               i:=0;
               repeat
                      if SL[i]='*' then
                         begin
                               SL[SL.Count-1]:=(FloatToStr(StrToFloat(SL[i-1])*StrToFloat(SL[i+1])));
                               {Zahl vor und nach dem * werden multipliziert und in die Zeile der ersten Zahl geschrieben???}
                               SL.Delete(SL.Count);
                               {Jetzt soll die Zeile des Operators gelöscht werden}
                               SL.Delete(SL.Count+1);{und die der zweiten Zahl}
                               i:=i+1;
                               {Geht zur Position des nächsten Rechenzeichens}
                               exit;
                               {Beendet, da wieder nach * gesucht werden soll->Neubeginn ab Repeat}
                         end;
                      if SL[i]='/' then
                         begin
                               SL[SL.Count-1]:=(FloatToStr(StrToFloat(SL[i-1])/StrToFloat(SL[i+1])));
                               SL.Delete(SL.Count);
                               SL.Delete(SL.Count+1);
                               i:=i+1;
                               exit;
                         end;
                      if SL[i]='+' then
                         begin
                               SL[SL.Count-1]:=(FloatToStr(StrToFloat(SL[i-1])+StrToFloat(SL[i+1])));
                               SL.Delete(SL.Count);
                               SL.Delete(SL.Count+1);
                               i:=i+1;
                               exit;
                         end;
                      if SL[i]='-' then
                         begin
                               SL[SL.Count-1]:=(FloatToStr(StrToFloat(SL[i-1])-StrToFloat(SL[i+1])));
                               SL.Delete(SL.Count);
                               SL.Delete(SL.Count+1);
                               i:=i+1;
                               exit;
                         end;
                     
               until SL.Count = 1;

Ich denke mal, das ist ziemlich falsch...
Also den ersten Teil hab ich jetzt verstanden (StringList erstellen), aber jetzt weiß ich nicht weiter :(
Bitte helft mir nochmal!!!


Liebe Grüße!

himitsu 23. Okt 2009 11:37

Re: Term (Zeichenfolge 1+2*3) in Fließkommazahl
 
Bei deiner Schleife würde i zuerst beim + vorbeikommen und dieses auflösen.

Jetzt aber einfach auf die Idee zu kommen die Schleife rückwärts laufen zu lassen wäre auch eine blöde Idee,
(bevor zu also danfängst darüber nachzudenken)
denn 1*2+3 würde dann wiederum falsch ausgewertet.



du mußt einzelne/mehere Schleifen nacheinander machen ... und zwar für alle Stufen einzeln


Vorzeichen (+-) - Punktoperationen (*/) - Strichoperationen (+-) ... genauso, wie man es auch auf'm Zettel selber machen würde

- in der ersten Schleife nur die Vorzeichen (+ und -)
also wenn "{keine Zahl, bzw. Operator} {+|-} {Zahl}"

- dann die nächste Stufe die Punkte (* und : aka /)
also wenn "{zahl} {*|/} {zahl}"

- und nun die Striche (+ und -)
also wenn "{zahl} {+|-} {zahl}"

siehe Beitrag #25

der Funktionsinterpreter vom Delphi-Treff kann das alles in nur einer Schleife machen, da er dort eine andere Verschachtelung der Zahlen und Operatoren genutzt wird.
* + 1 2 3
da kann man immer alles gleich ausrechnen, welches "{operator} {zahl} {zahl}" entspricht und muß auf keine Reihenfolge achten, da die Reihenfolge durch die Verschachtelung vorgegeben wird


also deinen Code erstmal in 2 Schleifen zerlegen
Delphi-Quellcode:
begin
               i:=0;
               while i < SL.Count do
                  begin
                      if SL[i]='*' then
                         begin
                               SL[SL.Count-1]:=(FloatToStr(StrToFloat(SL[i-1])*StrToFloat(SL[i+1])));
                               {Zahl vor und nach dem * werden multipliziert und in die Zeile der ersten Zahl geschrieben???}
                               SL.Delete(SL.Count);
                               {Jetzt soll die Zeile des Operators gelöscht werden}
                               SL.Delete(SL.Count);{und die der zweiten Zahl}
                               {etwas wurde gefunden - fange von vorn an}
                               i := 0;
                         end
                      else
                      if SL[i]='/' then
                         begin
                               SL[SL.Count-1]:=(FloatToStr(StrToFloat(SL[i-1])/StrToFloat(SL[i+1])));
                               SL.Delete(SL.Count);
                               SL.Delete(SL.Count);
                               i := 0;
                         end
                      else
                         i := i + 1;
                  end;
               i:=0;
               while i < SL.Count do
                  begin
                      if SL[i]='+' then
                         begin
                               SL[SL.Count-1]:=(FloatToStr(StrToFloat(SL[i-1])+StrToFloat(SL[i+1])));
                               SL.Delete(SL.Count);
                               SL.Delete(SL.Count);
                               i := 0;
                         end
                      else
                      if SL[i]='-' then
                         begin
                               SL[SL.Count-1]:=(FloatToStr(StrToFloat(SL[i-1])-StrToFloat(SL[i+1])));
                               SL.Delete(SL.Count);
                               SL.Delete(SL.Count);
                               i := 0;
                         end
                      else
                         i := i + 1;
                  end;
wenn am Ende mehr als ein Eintrag in SL vorliegt, dann stimmte etwas mit dem Term nicht, bzw. du hast etwas noch nicht umgewandelt (z.B. weil der Operator/Befehl noch nicht implementiert ist)
hier z.B. "1 + 2 2 * 3" würde dein Code jetzt "3 6" ausgeben, da die letzen 2 Einträge nicht behandelt werden


Alle Zeitangaben in WEZ +1. Es ist jetzt 00:33 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz