Delphi-PRAXiS
Seite 4 von 5   « Erste     234 5      

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)

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 23:37 Uhr.
Seite 4 von 5   « Erste     234 5      

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