Entwickler-Ecke

Algorithmen, Optimierung und Assembler - Problem mit MatheParser


F34r0fTh3D4rk - Di 06.02.07 20:41
Titel: Problem mit MatheParser
hi, ich habe einen kleinen rekursiven Matheparser geschrieben (ich weiß, die dinger sind net so gut, aber dafür relativ übersichtlich).

naja, probleme macht dieser ausdruck:


Quelltext
1:
10^2-(4+3*(4+5))*sin(90)                    


sin(90) darf nicht am ende stehen, weil dann knallts ;)

er rechnet:

Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
10^2-(4+3*(4+5))*sin(90)
10^2
10
2
(4+3*(4+5))*sin(90)
(4+3
(4

3
(4+5))*sin(90)
(4+5))
4+5)



dann kommt ein Fehler:

Zitat:

---------------------------
Project1
---------------------------
'4+5)' ist kein gültiger Gleitkommawert.
---------------------------
OK
---------------------------



ich kann mir den fehler nicht genau erklären, hier der source:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
function ParseEx(astr: string): single;
  function ParseA(op: char): stringbegin result := copy(astr, 1, pos(op, astr) - 1); end;
  function ParseB(op: char): stringbegin result := copy(astr, pos(op, astr) + 1, length(astr) - pos(op, astr)); end;
  function ExpCheck(op: char): integer;
  var
    i, count: integer;
  begin
    count := 0;
    for i := length(astr) downto 1 do
    begin
      if astr[i] = '(' then inc(count);
      if astr[i] = ')' then dec(count);
      if (count = 0and (astr[i] = op) then
      begin
       result := i;
       exit;
      end;
    end;
    result := 0;
  end;
begin
  form1.memo1.Lines.add(astr);
  if ExpCheck('+') > 0 then result := ParseEx(ParseA('+')) + ParseEx(ParseB('+')) else
  if ExpCheck('-') > 0 then result := ParseEx(ParseA('-')) - ParseEx(ParseB('-')) else
  if ExpCheck('*') > 0 then result := ParseEx(ParseA('*')) * ParseEx(ParseB('*')) else
  if ExpCheck('/') > 0 then result := ParseEx(ParseA('/')) / ParseEx(ParseB('/')) else
  if ExpCheck('^') > 0 then result := power(ParseEx(ParseA('^')), ParseEx(ParseB('^'))) else
  if copy(astr, 13) = 'sin' then
    result := sin(degtorad(ParseEx(copy(astr, 4, length(astr) - 3)))) else
  if copy(astr, 13) = 'cos' then
    result := cos(degtorad(ParseEx(copy(astr, 4, length(astr) - 3)))) else
  if copy(astr, 13) = 'tan' then
    result := tan(degtorad(ParseEx(copy(astr, 4, length(astr) - 3)))) else
  if (length(astr) > 0and (astr[1] = '('then
  begin
    astr := copy(astr, 2, length(astr) - 2);
    result := ParseEx(astr)
  end else
  if astr = '' then
    result := 0 else
      result := strtofloat(astr);
end;


vielen dank, mfg

Gelöst


Allesquarks - Di 06.02.07 23:32

müsste es nicht heißen:

10^2-(4+3*(4+5))*sin(90)
10^2
10
2
(4+3*(4+5))*sin(90)
(4+3*(4+5))
4+3*(4+5)//evt auch ohne diesen Zwischenschritt

4
3*(4+5)
3

(4+5)
4+5

4
5

sin(90)
sin
(90)
90


ssb-blume - Mi 07.02.07 11:01

Das Parsen sollte eine polnische Notation erzeugen, mal Googlen!
Bei der Klammerung, aber auch beim Wechsel der Prioritaet muss das Zwischenergebnis in einen Stack geschoben werden (mit Typ!!).
Achtung! ein allein stehendes - (oder +) am Anfang wird bei allen mir bekannten (veroeffentlichten) Parsern nicht beachtet!


Allesquarks - Mi 07.02.07 11:10

Doch bei meinem parser aber der erzeugt auch keinen Stack sondern nen Baum


ssb-blume - Mi 07.02.07 18:29

kann keinen Baum sehen, auch keinen Wald.
Das ist eine rekursive Anwendung!


F34r0fTh3D4rk - Mi 07.02.07 18:42

ich hab das ja soweit gelöst, jetzt fehlt mir nur noch die beachtung von vorzeichen bei multiplikation, division und bei potenzen ;)

mfg


BenBE - Mi 07.02.07 21:19

Kannst ja mal deine aktuelle Lösung posten ... Dann schauen wir einfach mal ...


F34r0fTh3D4rk - Do 08.02.07 21:16

Die Lösung:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
function ParseEx(astr: string): extended;
  // Prüfen, ob sich ein Operator in einer Klammer befindet
  function ExpCheck(op: char): integer;
  var
    i, count: integer;
  begin
    Result := 0;
    count := 0;
    for i := 1 to length(astr) do
    begin
      if astr[i] = '(' then inc(count);
      if astr[i] = ')' then dec(count);
      if (count = 0and (astr[i] = op) then
      begin
        result := i;
          if (i > 1AND (op in ['-''+']) Then
          begin
            if astr[i-1in ['+''-''*''/''^'then
            begin
              result := 0;
              continue;
            end;
          end;
          exit;
      end;
      if (Count < 0then
        Result := 0;
    end;
    if count <> 0 then
      result := 0;
  end;
  function ParseA(op: char): stringbegin result := copy(astr, 1, ExpCheck(op) - 1); end;
  function ParseB(op: char): stringbegin result := copy(astr, ExpCheck(op) + 1, length(astr) - ExpCheck(op)); end;
// mathematische Konstanten
const
  e = 2.71828182845904523536028747135266249;
begin
  // Klammern
  if (length(astr) = ExpCheck(')')) and (Copy(astr, 11) = '('then
    result := ParseEx(copy(astr, 2, ExpCheck(')') - 2)) else
// GRUNDRECHENARTEN
  if ExpCheck('+') > 0 then result := ParseEx(ParseA('+')) + ParseEx(ParseB('+')) else
  if ExpCheck('-') > 0 then result := ParseEx(ParseA('-')) - ParseEx(ParseB('-')) else
  if ExpCheck('*') > 0 then result := ParseEx(ParseA('*')) * ParseEx(ParseB('*')) else
  if ExpCheck('/') > 0 then result := ParseEx(ParseA('/')) / ParseEx(ParseB('/')) else
  // Potenzen
  if ExpCheck('^') > 0 then result := power(ParseEx(ParseA('^')), ParseEx(ParseB('^'))) else
// FUNKTIONEN
  // sin cos tan
  if copy(astr, 13) = 'sin' then
    result := sin(degtorad(ParseEx(copy(astr, 4, length(astr) - 3)))) else
  if copy(astr, 13) = 'cos' then
    result := cos(degtorad(ParseEx(copy(astr, 4, length(astr) - 3)))) else
  if copy(astr, 13) = 'tan' then
    result := tan(degtorad(ParseEx(copy(astr, 4, length(astr) - 3)))) else
  // sqrt
  if copy(astr, 14) = 'sqrt' then
    result := sqrt(ParseEx(copy(astr, 5, length(astr) - 4))) else
// KONSTANTEN
  // pi
  if copy(astr, 12) = 'pi' then
    result := pi else
  // e
  if copy(astr, 12) = 'e' then
    result := e else
  // Alle Zahlen + ''
  if astr = '' then
    result := 0 else
      result := strtofloat(astr);
end;


danke user profile iconBenBE