AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Parser Kommarechnung

Ein Thema von Delphi-Narr · begonnen am 4. Sep 2010 · letzter Beitrag vom 5. Sep 2010
Antwort Antwort
Benutzerbild von Delphi-Narr
Delphi-Narr

Registriert seit: 29. Aug 2009
Ort: Duisburg
437 Beiträge
 
Delphi 2007 Professional
 
#1

Parser Kommarechnung

  Alt 4. Sep 2010, 16:10
Delphi-Version: 2005
Hallo, ich habe folgenden Parser benutzt, doch leider beherrscht dieser keine Kommazahlen...
WIe muss ich da was ändern, damit er dies auch beherrscht???

Delphi-Quellcode:
function TermToFloat(Const Term:string):real;
  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);
              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;
Viele Grüße!
  Mit Zitat antworten Zitat
alzaimar
(Moderator)

Registriert seit: 6. Mai 2005
Ort: Berlin
4.956 Beiträge
 
Delphi 2007 Enterprise
 
#2

AW: Parser Kommarechnung

  Alt 4. Sep 2010, 17:45
Delphi-Quellcode:
....
        '0'..'9': begin
            if decimal>0 then begin
              number := number + Iord(c) - 48)/decimal;
              decimal := decimal*10;
            else
              number := 10 * number + ord(c) - 48;
            code := isNum;
          end;
        '.': if decimal>0 then
               raise Exception.Create('invalid decimal')
            else
               decimal := 10;
        '(': begin
            decimal := 0;
            number := _Eval(term, i);
            code := isNum;
          end;
        ' ': begin
            decimal := 0;
            continue;
          end;
        '+', '-', '*', '/', ')':
          begin
            decimal := 0;
....
dann noch decimal deklarieren und initialisieren sowie die Zahlentypen von INTEGER nach DOUBLE ändern.

Ungetestet, könnte aber klappen.
"Wenn ist das Nunstruck git und Slotermeyer? Ja! Beiherhund das Oder die Flipperwaldt gersput!"
(Monty Python "Joke Warefare")
  Mit Zitat antworten Zitat
Benutzerbild von Delphi-Narr
Delphi-Narr

Registriert seit: 29. Aug 2009
Ort: Duisburg
437 Beiträge
 
Delphi 2007 Professional
 
#3

AW: Parser Kommarechnung

  Alt 4. Sep 2010, 19:53
Danke erstmal. Nach Korrektur einiger Tippfehler komme ich an einer Stelle nicht weiter. Hier nochmal alles (neu)

Delphi-Quellcode:
function TermToFloat(Const Term:string):real;
  function _Eval(term: string; var i: Integer): Integer;
  const
    isNone = 0;
    isNum = 1;
    isOp = 2;

  var
    numStack: array[0..10] of Double;
    number, code, sign, sp, j, decimal: Double;
    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
            if decimal>0 then begin
              number := (number + ord(c) - 48)/decimal;
              decimal := decimal*10;
              end
            else
              number := 10 * number + ord(c) - 48;
            code := isNum;
          end;
        '.': if decimal>0 then
               raise Exception.Create('invalid decimal')
            else
               decimal := 10;
        '(': begin
            decimal := 0;
            number := _Eval(term, i);
            code := isNum;
          end;
        ' ': begin
            decimal := 0;
            continue;
          end;
        '+', '-', '*', '/', ')':
          begin
            decimal := 0;
            if code = isNum then begin
              numStack[SP] := sign * number; // Hier kommt der Fehler (s.u.)
              sign := 1;
              inc(SP);
              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;

An der im Quelltext markierten Stelle kommt der Fehler
"inkompatible Typen: Integer und Double"

Ist doch alles Double?!

Viele Grüße!
  Mit Zitat antworten Zitat
daywalker9

Registriert seit: 1. Jan 2010
Ort: Leer
594 Beiträge
 
Delphi XE3 Professional
 
#4

AW: Parser Kommarechnung

  Alt 4. Sep 2010, 19:57
Delphi-Quellcode:
...
sp : double //muss integer sein
..
Lars
  Mit Zitat antworten Zitat
Benutzerbild von Delphi-Narr
Delphi-Narr

Registriert seit: 29. Aug 2009
Ort: Duisburg
437 Beiträge
 
Delphi 2007 Professional
 
#5

AW: Parser Kommarechnung

  Alt 4. Sep 2010, 20:30
Oh, und wie soll ich decimal initialisieren? Also als was?

Viele Grüße!
  Mit Zitat antworten Zitat
daywalker9

Registriert seit: 1. Jan 2010
Ort: Leer
594 Beiträge
 
Delphi XE3 Professional
 
#6

AW: Parser Kommarechnung

  Alt 4. Sep 2010, 20:50
Delphi-Quellcode:
 function _Eval(term: string; var i: Integer): Integer;
  const
    isNone = 0;
    isNum = 1;
    isOp = 2;

  var
    numStack: array[0..10] of Double;
    number, code, sign, j, decimal: Double;
    Ops: string;
    sp : integer;
    c: Char;
Lars
  Mit Zitat antworten Zitat
Benutzerbild von Delphi-Narr
Delphi-Narr

Registriert seit: 29. Aug 2009
Ort: Duisburg
437 Beiträge
 
Delphi 2007 Professional
 
#7

AW: Parser Kommarechnung

  Alt 4. Sep 2010, 20:54
So meinte ich das nicht. Ich meine den Startwert...

Ich krieg immer eine Fehlermeldung, einen Gleitkommaüberlauf...

decimal ist dabei =2.1219942e-314
number ist 0 und c=5

Fehlerzeile ist

number := (number + ord(c) - 48)/decimal;
  Mit Zitat antworten Zitat
Namenloser

Registriert seit: 7. Jun 2006
Ort: Karlsruhe
3.724 Beiträge
 
FreePascal / Lazarus
 
#8

AW: Parser Kommarechnung

  Alt 4. Sep 2010, 21:17
Versuch mal, alzaimars Code nachzuvollziehen. Die Potenzen steigen in unserem Dezimalsystem von rechts nach links jeweils um eins an:
Code:
1. Stelle vor dem Komma: 10^0 = __1
2. Stelle vor dem Komma: 10^1 = _10
3. Stelle vor dem Komma: 10^2 = 100
(_ = Leerzeichen, aus irgendeinem Grund bleibt hier ja der Whitespace leider nicht mehr erhalten...)
usw...
D.h., man muss die Ziffer an der jeweilgen Position immer mit der jeiligen Zehner-Potenz, der Wertigkeit, multiplizieren, um den Wert zu erhalten. Deshalb funktioniert es bei von links nach rechts eingelesenen Ganzzahlen, einfach immer den alten Wert mit 10 zu multiplizieren und die aktuelle Ziffer zu addieren. So macht es auch der originale Algorithmus.

Hinter dem Komma ist es eben genau umgekehrt:
Code:
1. Stelle nach dem Komma: 10^-1 = 0.1
2. Stelle nach dem Komma: 10^-2 = 0.01
3. Stelle nach dem Komma: 10^-3 = 0.001
usw...
Hier braucht man allerdings eine zusätzliche Variable, die die Potenz speichert: Dafür ist bei Alzaimars Code die Variavble decimal zuständig, die für jede Ziffer nach dem Komma mit 10 multipliziert wird. Die neue Ziffer wird dann durch diesen Wert geteilt und zum vorigen Wert hinzuaddiert. Der Initialwert ist logischerweise 10, weil die erste Stelle nach dem Komma die Wertigkeit 1/10 hat (10^-1).

Dieser Wert wird initialisiert, sobald das erste mal ein Komma auftritt:
Delphi-Quellcode:
        '.': if decimal>0 then
               raise Exception.Create('invalid decimal')
            else
               decimal := 10;
Die Prüfung auf decimal>0 ist deshalb vorhanden, weil sonst mehrere Kommata pro zahl vorkommen dürften, was natürlich sinnlos wäre. Lange Rede kurz: Der Algorithmus geht von einem mit 0 initialisierten Wert aus.

Da hättest du mit ein paar Sekunden Analyse aber auch drauf kommen können...
  Mit Zitat antworten Zitat
alzaimar
(Moderator)

Registriert seit: 6. Mai 2005
Ort: Berlin
4.956 Beiträge
 
Delphi 2007 Enterprise
 
#9

AW: Parser Kommarechnung

  Alt 4. Sep 2010, 22:54
Versuch mal, alzaimars Code nachzuvollziehen...Der Algorithmus geht von einem mit 0 initialisierten Wert aus...Da hättest du mit ein paar Sekunden Analyse aber auch drauf kommen können...
Correctamente, zumal "decimal := 0;" auch in der Schleife bei jedem erkannten nicht-numersichen Symbol ausgeführt ist.

Delphi-Narr, Du hast Dir den Spass genommen, das alleine rauszufinden. So geht das aber nunmal beim Programmieren: Man muss selbst Dreck fressen und rumheulen, bis man von selbst auf die Lösung kommt. Nur so wird man wirklich gut.

Nächstes mal probierst Du selbst rum und versuchst, das Problem zu verstehen, ok?
"Wenn ist das Nunstruck git und Slotermeyer? Ja! Beiherhund das Oder die Flipperwaldt gersput!"
(Monty Python "Joke Warefare")
  Mit Zitat antworten Zitat
Benutzerbild von Delphi-Narr
Delphi-Narr

Registriert seit: 29. Aug 2009
Ort: Duisburg
437 Beiträge
 
Delphi 2007 Professional
 
#10

AW: Parser Kommarechnung

  Alt 5. Sep 2010, 11:35
Ich habe das auch gemacht (also decimal:=0), klappt aber nicht richtig (rechnet falsch)...
Bei 1,1 * 9 kommt nicht 9,9 raus sondern 0,4 oder so etwas...



Ich habe mal einen komplett neuen geschrieben, der klappt jetzt auch...

Der schreibt den Term nach Zahlen und Operatoren sortiert in eine Liste, fügt oder löscht falsche Rechenzeichen (hinzu), geht dann den Term von HINTEN durch und sucht nach Klammern, löst diese auf. Dann normal nach Potemzen suchen und auflösen und dann Punkt vor Strich.

Ich hätte mir zwar das normale auflösen (nach den Klammern) sparen können, indem ich einfach Klammern aufrufe, aber aus Testzwecken habe ich das stehen lassen (um die Zehnmilliarden Fehler beim durchgehen zu umgehen).

Hier mein Ergebnis:

Delphi-Quellcode:
function TermTOFloat(Term:string):real;


function Klammern(T:string):string;
Var SL: TStringList;
    i: Integer;
    S: string;

Begin

    {Falls das erste Zeichen ein Rechenzeichen wie + oder - ist, so wird eine Null eingefügt,
    um die Rechnung fortzusetzen (Beim Rechnen würde sonst ein Fehler auftreten)}

    S:=T;
    case S[1] of
    '+','-':
    begin
         insert('0',S,1);
    end;

    {Die Rechenzeichen * und / werden gelöscht}
    
    '*','/':
    begin
         Delete(S,1,1);
    end;
    end;

    //Rechenzeichen am Ende werden gelöscht
    
    case S[Length(S)] of
    '+','-','*','/':
    begin
         Delete(S,Length(S),1);
    end;
    end;

    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;


          {Ab hier wird gerechnet}
          begin
               begin
               i:=0;
               while i < SL.Count do
                  begin
                      if SL[i]='^then
                         begin
                               SL[i-1]:=(FloatToStr(Power(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(i);
                               {Jetzt soll die Zeile des Operators gelöscht werden}
                               SL.Delete(i);{und die der zweiten Zahl}
                               {etwas wurde gefunden - fange von vorn an} 
                               i := i-1;
                         end
                      else
                         i := i + 1;
                  end;
               
               i:=0;
               while i < SL.Count do
                  begin
                      if SL[i]='*then
                         begin
                               SL[i-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(i);
                               {Jetzt soll die Zeile des Operators gelöscht werden} 
                               SL.Delete(i);{und die der zweiten Zahl} 
                               {etwas wurde gefunden - fange von vorn an} 
                               i := i-1;
                         end
                      else
                      if SL[i]='/then
                         begin
                               SL[i-1]:=(FloatToStr(StrToFloat(SL[i-1])/StrToFloat(SL[i+1])));
                               SL.Delete(i);
                               SL.Delete(i);
                               i := i-1;
                         end
                      else
                         i := i + 1;
                  end;
               i:=0;
               while i < SL.Count do
                  begin
                      if SL[i]='+then
                         begin
                               SL[i-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(i);
                               {Jetzt soll die Zeile des Operators gelöscht werden} 
                               SL.Delete(i);{und die der zweiten Zahl} 
                               {etwas wurde gefunden - fange von vorn an} 
                               i := i-1;
                         end
                      else
                      if SL[i]='-then
                         begin
                               SL[i-1]:=(FloatToStr(StrToFloat(SL[i-1])-StrToFloat(SL[i+1])));
                               SL.Delete(i);
                               SL.Delete(i);
                               i := i-1;
                         end
                      else
                         i := i + 1;
                  end;


          end;
          end;
      Finally
            
        Result := SL[0];
        end;
    Finally
      SL.Free;
    End;

end;
Var SL: TStringList;
    i,p,j,a: Integer;
    S,tmp: string;

  Begin
     
            Result:=0;
    {Falls das erste Zeichen ein Rechenzeichen wie + oder - ist, so wird eine Null eingefügt,
    um die Rechnung fortzusetzen (Beim Rechnen würde sonst ein Fehler auftreten)}

    S:=Term;
    case S[1] of
    '+','-':
    begin
         insert('0',S,1);
    end;

    {Die Rechenzeichen * und / werden gelöscht}
    
    '*','/':
    begin
         Delete(S,1,1);
    end;
    end;

    //Rechenzeichen am Ende werden gelöscht
    
    case S[Length(S)] of
    '+','-','*','/':
    begin
         Delete(S,Length(S),1);
    end;
    end;

    //Klammern durchsuchen und prüfen, ob ein Rechenzeichen davor steht, sonst * einfügen

    for i:=2 to Length(S) do
    begin
         if (S[i]='(') and (pos(S[i-1],'+-*/')=0) then insert('*',S,i);
    end;


    
    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;

        //Klammern suchen und auflösen
        i:=SL.count-1;
        while i>=0 do
        begin
             if SL[i]='(then
             begin
                  p:=i;
                  tmp:='';
                  j:=p+1;
                  while (SL[j]<>')') do
                  begin
                       tmp:=tmp+SL[j];
                       j:=j+1;
                       if j=SL.count then break;
                  end;
                  if j=SL.count then j:=j-1;

                  tmp:=Klammern(tmp);
                  SL[p]:=tmp;
                  //Löschen
                  a:=p+1;

                  for p:=P+1 to j do
                      begin
                           SL.Delete(a);
                      end;
             end;

             i:=i-1;
        end;
        tmp:='';
        for i:=0 to SL.count-1 do
            tmp:=tmp+SL[i];

                  {Ab hier wird gerechnet}
          begin
               begin
               i:=0;
               while i < SL.Count do
                  begin
                      if SL[i]='^then
                         begin
                               SL[i-1]:=(FloatToStr(Power(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(i);
                               {Jetzt soll die Zeile des Operators gelöscht werden} 
                               SL.Delete(i);{und die der zweiten Zahl}
                               {etwas wurde gefunden - fange von vorn an} 
                               i := i-1;
                         end
                      else
                         i := i + 1;
                  end;

               i:=0;
               while i < SL.Count do
                  begin
                      if SL[i]='*then
                         begin
                               SL[i-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(i);
                               {Jetzt soll die Zeile des Operators gelöscht werden}
                               SL.Delete(i);{und die der zweiten Zahl} 
                               {etwas wurde gefunden - fange von vorn an} 
                               i := i-1;
                         end
                      else
                      if SL[i]='/then
                         begin
                               SL[i-1]:=(FloatToStr(StrToFloat(SL[i-1])/StrToFloat(SL[i+1])));
                               SL.Delete(i);
                               SL.Delete(i);
                               i := i-1;
                         end
                      else
                         i := i + 1;
                  end;
               i:=0;
               while i < SL.Count do
                  begin
                      if SL[i]='+then
                         begin
                               SL[i-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(i);
                               {Jetzt soll die Zeile des Operators gelöscht werden}
                               SL.Delete(i);{und die der zweiten Zahl} 
                               {etwas wurde gefunden - fange von vorn an} 
                               i := i-1;
                         end
                      else
                      if SL[i]='-then
                         begin
                               SL[i-1]:=(FloatToStr(StrToFloat(SL[i-1])-StrToFloat(SL[i+1])));
                               SL.Delete(i);
                               SL.Delete(i);
                               i := i-1;
                         end
                      else
                         i := i + 1;
                  end;

                   
          end;
          end;
      Finally

        tmp:=SL[0];

        Try
           Result := StrToFloat(SL[0]);
        except
            Result:=0;
        end;

        end;
    Finally
      SL.Free;
    End;
end;
Viele Grüße und viel Spaß damit

Geändert von Delphi-Narr ( 5. Sep 2010 um 11:44 Uhr)
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 18:18 Uhr.
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