Forum: PC-Programmierung Infix to Postfix


von ldn (Gast)


Lesenswert?

Hallo Forum

ich muss einen infix-to-postfix-"Parser" programmieren.
mein jetztiger code funktioniert auch schon fast (die stack-funktionen 
hab ich jz mal weggelassen):
1
int priority(char c) {
2
  if (c == '/' || c == '*')
3
    return 2;
4
  if (c == '+' || c == '-')
5
    return 1;
6
  return 0;
7
}
8
9
void infix2postfix(char *buffer, const char *term) {
10
  char x;
11
  while (*term) {
12
    if (isalnum(*term))
13
      *buffer++ = *term;
14
      //printf("%c", *term);
15
    else if (*term == '(')
16
      push('(');
17
    else {
18
      if (*term == ')')
19
        while ((x = pop()) != '(') {
20
          *buffer++ = x;
21
          //printf("%c", x);
22
        }
23
      else {
24
        while (priority(*term) <= priority(top()) && !isEmpty()) {
25
          x = pop();
26
          *buffer++ = x;
27
          //printf("%c", x);
28
        }
29
        push(*term);
30
      }
31
    }
32
    term++;
33
  }
34
  while (!isEmpty()) {
35
    x = pop();
36
    *buffer++ = x;
37
    //printf("%c", x);
38
  }
39
  *buffer = '\0';
40
}

Es klappt eig ganz gut, nur:
Infix: (2+30)*4^25 <-- Eingabe
Postfix: 230+425^* <-- Ausgabe
Man sieht: die Zahlen werde nicht richtig getrennt. Ist ja uach klar, 
ich füge nirgends ein ' ' ein. Nur ich weiß jetzt nicht wie, oder eher 
wo, ich das Leerzeichen einfügen muss.

Wenn man den Code oben anders effektiver schreiben kann, gerne Tipps 
dazu.

Danke für euere Hilfe

von Karl H. (kbuchegg)


Lesenswert?

ldn schrieb:

> Es klappt eig ganz gut, nur:
> Infix: (2+30)*4^25 <-- Eingabe
> Postfix: 230+425^* <-- Ausgabe
> Man sieht: die Zahlen werde nicht richtig getrennt. Ist ja uach klar,
> ich füge nirgends ein ' ' ein. Nur ich weiß jetzt nicht wie, oder eher
> wo, ich das Leerzeichen einfügen muss.

Das kannst du so auch nicht, denn du unterscheidest in deiner 
übergeordneten Schleife hier
1
  while (*term) {
2
    if (isalnum(*term))
3
      *buffer++ = *term;
4
...
nicht, ob damit eine Zahl komplett ist oder nicht, sondern du behandelst 
das alles, als ob immer nur 1 Zeichen relevant ist.
Mit anderen Worten: dein Parser kann nicht erkennen, dass eine 'Einheit' 
(Zahl, Variablennamen, Operator, ...) aus mehreren Zeichen bestehen 
könnte, sondern geht immer davon aus, dass es sich beim nächsten zu 
parsenden Symbol um genau 1 Zeichen handelt.

Deshalb agieren echte Parser auch nicht in dieser Form direkt auf dem 
Quelltext sondern benutzen einen sog. lexikalischen Parser, der ihnen 
das jeweils nächste Symbol zum Parsen heraus holt. So ein lexiaklischer 
Parser untersucht die jeweils nächsten Ccaracter aus der EIngabe und 
stellt zb fest, dass da jetzt 3 Ziffer-Character hintereinander kommen, 
die er zu einer Zahl zusammenfassen soll. Der Syntaxparser kriegt vom 
lexikalischen Parser dann die Information, dass als nächstes Symbol in 
der Eingabe eine Zahl vorhanden ist (deren Wert beispielsweise 385 
beträgt), oder das in der EIngabe als nächstes Symbol ein 'kleiner 
gleich' Symbol vorhanden ist.

D.h. für den Parser gestaltet sich die Extraktion des nächsten Symbols 
aus der Eingabe nicht mehr so einfach
1
  while (*term) {
dadurch, dass er *term betrachtet, sondern er überlässt diese Analyse 
dem lexikalischen Parser, der sich die jeweils nächsten Character 
ansieht und entscheidet ob und wie die zusammenghören
1
  while ( nextSymbol( &term, &sym, &value ) ) {
2
3
    if( sym == NUMBER )
4
      push_number( value );
5
6
    else if( sym == OPERATOR )
7
      push_operator( value );  // value ist ein Code für beispielsweise die + Operation
8
....

Jetzt wird dein ganzer Aufbau schon ein wenig komplexer, weil du nicht 
einfach nur 1-buchstabige Dinge auf deinem Stack verwalten musst, 
sondern weil da jetzt ein buntes Mischmasch aus Zahlen und Operationen 
auf dem Stack verwaltet werden muss.
Dazu brauchst du aber dann auch eine entsprechende Datenstruktur, mit 
der du das kannst. Dein Stack ist dann nicht mehr einfach nur ein Array 
von char. Sondern dein Stack könnte dann zb so aussehen
1
struct exprStackItem
2
{
3
  unsigned char itemType;
4
  int           itemValue;
5
};
6
7
struct exprStackItem exprStack[50];    // Platz für 50 einträge am Stack
8
int exprStackPointer;
9
10
#define STACK_NUMBER_ITEM         1
11
#define STACK_OPERATOR_ITEM       2
12
#define STACK_OPEN_PAREN_ITEM     3
13
14
void pushNumber( int value )
15
{
16
  exprStack[exprStackPointer].itemType = STACK_NUMBER_ITEM;
17
  exprStack[exprStackPointer++].itemValue = value;
18
}
19
20
void pushOperator( int operatorCode )
21
{
22
  exprStack[exprStackPointer].itemType = STACK_OPERATOR_ITEM;
23
  exprStack[exprStackPointer++].itemValue = value;
24
}
25
26
void pushOpenParen( void )
27
{
28
  exprStack[exprStackPointer].itemType = STACK_OPEN_PAREN_ITEM;
29
}
30
31
....

Aber: dadurch, dass du die Dinge jetzt sauber auf dem Stack liegen hast, 
kannst du zb die angegebenen Ausdrücke auch tatsächlich irgendwann 
ausrechnen. Dein eine Integer-Zahl ist bei dir am Stack jetzt auch 
wirklich eine Integer Zahl und nicht einfach nur einzelne 'Buchstaben'.

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

Hier hat sich außerdem ein Fehler eingeschlichen:

1
          while (priority(*term) <= priority(top()) && !isEmpty()) {

Du musst die beiden Operanden der &&-Verknüpfung vertauschen, denn du
willst zuerst sicherstellen, dass der Stack mindestens ein Element
enthält, bevor dieses mit top() gelesen wird:

1
          while (!isEmpty() && priority(*term) <= priority(top())) {

von ldn (Gast)


Lesenswert?

Okay, danke schonmal ihr beide.

Ich probiere noch ein bischen herum und wenn sich weitere Fragen auftun 
komme ich hierher zurück.

Ich habe noch was gefunden: 
https://en.wikipedia.org/wiki/Shunting-yard_algorithm
Wäre ja das was ich will

von Karl H. (kbuchegg)


Lesenswert?

ldn schrieb:

> Ich habe noch was gefunden:
> https://en.wikipedia.org/wiki/Shunting-yard_algorithm
> Wäre ja das was ich will

Aber auch hier gilt
1
To convert, the program reads each symbol in order and does something based on that symbol.

'symbol'(!)
nicht Einzelbuchstaben. Egal wie du es drehst und wendest, 
sinnvollerweise kommst du um einen lexikalischen Parser nicht herum. Der 
ist ja nicht weiter schwer zu schreiben. Eine Zahl ist etwas, das mit 
einer Ziffer beginnt und aus weiteren Ziffern besteht, bis zum ersten 
Character der eben keine Ziffer mehr ist (für Integer. Bei Floating 
Point Zahlen wird das dann schon aufwändiger)
In deinem obigen Beispiel nimmst du noch für ')' und '(' ein eigenes 
Symbol, weil die in deinem Stack basiertem Code eine besondere Bedeutung 
haben. Ok, das müsste man nicht unbedingt.

und alles andere ist dann einfach ein Operator-Symbol

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.