web-dev-qa-db-fra.com

Conversion d'une expression infixe en postfixe (C++) à l'aide de Stacks

Mon conférencier m'a chargé de créer un programme permettant de convertir et d'infixer l'expression en postfixe à l'aide de Stacks. J'ai créé les classes de pile et certaines fonctions pour lire l'expression infixe. 

Mais cette fonction, appelée convertToPostfix(char * const inFix, char * const postFix) qui est chargée de convertir l'expression inFix du tableau inFix en expression post fix dans le tableau postFix à l'aide de piles, ne fait pas ce qu'elle est supposée faire. Pouvez-vous m'aider et me dire ce que je fais mal?

Ce qui suit est un code dans lequel les fonctions de conversion d'inFix en postFix sont et convertToPostfix(char * const inFix, char * const postFix) est ce que j'ai besoin d'aide pour la correction:

 void ArithmeticExpression::inputAndConvertToPostfix()
    {
       char inputChar; //declaring inputChar
       int i = 0; //inizalize i to 0

       cout << "Enter the Arithmetic Expression(No Spaces): ";

       while( ( inputChar = static_cast<char>( cin.get() ) ) != '\n' )
       {
          if (i >= MAXSIZE) break; //exits program if i is greater than or equal to 100

          if(isdigit(inputChar) || isOperator(inputChar))
          {
             inFix[i] = inputChar; //copies each char to inFix array
             cout << inFix[i] << endl;
          }
          else
             cout << "You entered an invalid Arithmetic Expression\n\n" ;

          }

          // increment i;
          i++;
          convertToPostfix(inFix, postFix);


       }




    bool ArithmeticExpression::isOperator(char currentChar)
    {

        if(currentChar == '+')
            return true;
        else if(currentChar == '-')
            return true;
        else if(currentChar == '*')
            return true;
        else if(currentChar == '/')
            return true;
        else if(currentChar == '^')
            return true;
        else if(currentChar == '%')
            return true;
        else
            return false;
    }

    bool ArithmeticExpression::precedence(char operator1, char operator2)
    {
        if ( operator1 == '^' )
           return true;
        else if ( operator2 == '^' )
           return false;
        else if ( operator1 == '*' || operator1 == '/' )
           return true;
        else if ( operator1 == '+' || operator1 == '-' )
           if ( operator2 == '*' || operator2 == '/' )
              return false;
           else
              return true;

        return false;
    }

   void ArithmeticExpression::convertToPostfix(char * const inFix, char * const postFix)
        {
           Stack2<char> stack;

           const char lp = '(';

           stack.Push(lp); //Push a left parenthesis ‘(‘ onto the stack.

           strcat(inFix,")");//Appends a right parenthesis ‘)’ to the end of infix.

          // int i = 0;
           int j = 0;

           if(!stack.isEmpty())
           {

               for(int i = 0;i < 100;){

                   if(isdigit(inFix[i]))
                   {
                        postFix[j] = inFix[i];
                        cout << "This is Post Fix for the first If: " << postFix[j] << endl;
                        i++;
                        j++;
                   }

                    if(inFix[i] == '(')
                   {
                       stack.Push(inFix[i]);
                       cout << "The InFix was a (" << endl;
                       i++;
                       //j++;
                   }

                    if(isOperator(inFix[i]))
                               {
                            char operator1 = inFix[i];

                            cout << "CUrrent inFix is a operator" << endl;
                                   if(isOperator(stack.getTopPtr()->getData()))
                                       {
                                       cout << "The stack top ptr is a operator1" << endl;
                                       char operator2 = stack.getTopPtr()->getData();
                                           if(precedence(operator1,operator2))
                                           {
                                               //if(isOperator(stack.getTopPtr()->getData())){
                                                   cout << "The stack top ptr is a operato2" << endl;
                                                   postFix[j] = stack.pop();
                                                   cout << "this is post fix " << postFix[j] << endl;
                                                   i++;
                                                   j++;
                                              // }

                                           }

                                       }
                                   else

                                       stack.Push(inFix[i]);
                                   // cout << "Top Ptr is a: "<< stack.getTopPtr()->getData() << endl;



                               }

                    for(int r = 0;r != '\0';r++)
                        cout << postFix[r] << " ";

                        if(inFix[i] == ')')
                       {
                           while(stack.stackTop()!= '(')
                         {
                               postFix[j] = stack.pop();
                               i++;
                               j++;
                                }
                           stack.pop();

                            }
                       }
           }

                   }

Notez que la fonction convertToPostfix a été créée en utilisant cet algorithme:

  • Placez une parenthèse gauche "(" sur la pile.
  • Ajoutez une parenthèse droite «)» à la fin de l'infixe.
  • Tant que la pile n'est pas vide, lisez infixe de gauche à droite et procédez comme suit:

    • Si le caractère actuel dans infixe est un chiffre, copiez-le dans l'élément suivant de postfix.
    • Si le caractère actuel dans infixe est une parenthèse gauche, placez-le dans la pile.
    • Si le personnage actuel dans infix est un opérateur,

      • Faites glisser le ou les opérateurs (s'il y en a) en haut de la pile alors qu'ils ont une priorité égale ou supérieure à celle de l'opérateur actuel, puis insérez les opérateurs vidés dans postfix.
      • Poussez le caractère actuel infixé sur la pile.
    • Si le caractère actuel dans infix est une parenthèse droite
      • Faites éclater les opérateurs du haut de la pile et insérez-les dans le suffixe jusqu’à ce qu’une parenthèse de gauche se trouve en haut de la pile.
      • Retirez (et supprimez) la parenthèse gauche de la pile.
9
Reggie Escobar

Ceci est fondamentalement un commentaire à la réponse de Yuushi.

  • La boucle while externe (! Stack.empty ()) est incorrecte. il suffit de l'enlever. (gardez le corps de boucle ofc). A la fin de la fonction, vérifiez que la pile est vide, sinon l'expression aurait des erreurs de syntaxe.
  • Comme Yuushi l'a déjà dit, la fonction de préséance semble fausse. D'abord, vous devriez donner aux paramètres de meilleurs noms: l'un est l'opérateur à gauche et l'autre à droite. (En ce moment, vous l'appelez precedence(rightOp, leftOp)). Ensuite, vous devriez documenter le résultat - vous retournez true si a rOp b lOp c == (a rOp b) lOp c (oui, la commande de l'opérateur ne correspond pas à celle que vous appelez - "+" et "-" ne sont pas identiques dans les deux commandes, par exemple).
  • Si vous trouvez un nouvel opérateur, vous devez parcourir les anciens opérateurs de la pile, par exemple, après avoir lu a - b * c, votre sortie est a b c et la pile est [- *]. maintenant, vous lisez un + et vous devez afficher les deux opérateurs, ce qui donne a b c * -. C'est-à-dire que l'entrée a - b * c + d devrait donner a b c * - d +

Mise à jour : solution complète annexée (basée sur la réponse de Yuushi): 

bool isOperator(char currentChar)
{
    switch (currentChar) {
    case '+':
    case '-':
    case '*':
    case '/':
    case '^':
    case '%':
        return true;
    default:
        return false;
    }
}

// returns whether a `lOp` b `rOp` c == (a `lOp` b) `rOp` c
bool precedence(char leftOperator, char rightOperator)
{
    if ( leftOperator == '^' ) {
        return true;
    } else if ( rightOperator == '^' ) {
        return false;
    } else if ( leftOperator == '*' || leftOperator == '/' || leftOperator == '%' ) {
        return true;
    } else if ( rightOperator == '*' || rightOperator == '/' || rightOperator == '%' ) {
        return false;
    }

    return true;
}

#include <stdexcept>
#include <cctype>
#include <sstream>
#include <stack>
std::string convertToPostfix(const std::string& infix)
{
    std::stringstream postfix; // Our return string
    std::stack<char> stack;
    stack.Push('('); // Push a left parenthesis ‘(‘ onto the stack.

    for(std::size_t i = 0, l = infix.size(); i < l; ++i) {
        const char current = infix[i];

        if (isspace(current)) {
            // ignore
        }
        // If it's a digit or '.' or a letter ("variables"), add it to the output
        else if(isalnum(current) || '.' == current) {
            postfix << current;
        }

        else if('(' == current) {
            stack.Push(current);
        }

        else if(isOperator(current)) {
            char rightOperator = current;
            while(!stack.empty() && isOperator(stack.top()) && precedence(stack.top(), rightOperator)) {
                postfix << ' ' << stack.top();
                stack.pop();
            }
            postfix << ' ';
            stack.Push(rightOperator);
        }

        // We've hit a right parens
        else if(')' == current) {
            // While top of stack is not a left parens
            while(!stack.empty() && '(' != stack.top()) {
                postfix << ' ' << stack.top();
                stack.pop();
            }
            if (stack.empty()) {
                throw std::runtime_error("missing left paren");
            }
            // Discard the left paren
            stack.pop();
            postfix << ' ';
        } else {
            throw std::runtime_error("invalid input character");
        }
    }


    // Started with a left paren, now close it:
    // While top of stack is not a left paren
    while(!stack.empty() && '(' != stack.top()) {
        postfix << ' ' << stack.top();
        stack.pop();
    }
    if (stack.empty()) {
        throw std::runtime_error("missing left paren");
    }
    // Discard the left paren
    stack.pop();

    // all open parens should be closed now -> empty stack
    if (!stack.empty()) {
        throw std::runtime_error("missing right paren");
    }

    return postfix.str();
}

#include <iostream>
#include <string>
int main()
{
    for (;;) {
        if (!std::cout.good()) break;
        std::cout << "Enter the Arithmetic Expression: ";
        std::string infix;
        std::getline(std::cin, infix);
        if (infix.empty()) break;

        std::cout << "Postfix: '" << convertToPostfix(infix) << "'\n";
    }

    return 0;
}
6
Stefan

Il y a donc un certain nombre de problèmes avec votre code. Je posterai ce que devrait (devrait être) une solution corrigée, qui contient de nombreux commentaires pour expliquer ce qui se passe et où vous avez commis des erreurs. Quelques petites choses à l’avant:

  1. J'utiliserai std::string au lieu de char * car cela rend les choses beaucoup plus propres et honnêtement, vous devriez l'utiliser dans C++ à moins que vous n'ayez une très bonne raison de ne pas le faire (comme l'interopérabilité avec une bibliothèque C.). Cette version retourne également une string au lieu de prendre un char * en tant que paramètre.

  2. J'utilise la pile de la bibliothèque standard, <stack>, qui est légèrement différente de celle que vous avez créée. top() vous montre le prochain élément sans en le retirant de la pile, et pop() renvoie void, mais supprime l’élément supérieur de la pile.

  3. C'est une fonction gratuite, qui ne fait pas partie d'un cours, mais qui devrait être facile à modifier - c'est tout simplement plus facile pour moi de tester de cette façon.

  4. Je ne suis pas convaincu que vos tableaux de priorité des opérateurs soient corrects, cependant, je vous laisserai le vérifier.


#include <stack>
#include <cctype>
#include <iostream>

std::string convertToPostfix(std::string& infix)
{
    std::string postfix; //Our return string
    std::stack<char> stack;
    stack.Push('('); //Push a left parenthesis ‘(‘ onto the stack.
    infix.Push_back(')');

    //We know we need to process every element in the string,
    //so let's do that instead of having to worry about
    //hardcoded numbers and i, j indecies
    for(std::size_t i = 0; i < infix.size(); ++i) {

        //If it's a digit, add it to the output
        //Also, if it's a space, add it to the output 
        //this makes it look a bit nicer
        if(isdigit(infix[i]) || isspace(infix[i])) {
            postfix.Push_back(infix[i]);
        }

        //Already iterating over i, so 
        //don't need to worry about i++
        //Also, these options are all mutually exclusive,
        //so they should be else if instead of if.
        //(Mutually exclusive in that if something is a digit,
        //it can't be a parens or an operator or anything else).
        else if(infix[i] == '(') {
            stack.Push(infix[i]);
        }

        //This is farily similar to your code, but cleaned up. 
        //With strings we can simply Push_back instead of having
        //to worry about incrementing some counter.
        else if(isOperator(infix[i]))
        {
            char operator1 = infix[i];
            if(isOperator(stack.top())) {
                while(!stack.empty() && precedence(operator1,stack.top())) {
                    postfix.Push_back(stack.top());
                    stack.pop();
                }
            }
            //This shouldn't be in an else - we always want to Push the
            //operator onto the stack
            stack.Push(operator1);
        }    

        //We've hit a right parens - Why you had a for loop
        //here originally I don't know
        else if(infix[i] == ')') {
            //While top of stack is not a right parens
            while(stack.top() != '(') {
            //Insert into postfix and pop the stack
                postfix.Push_back(stack.top());
                stack.pop();
            }
            // Discard the left parens - you'd forgotten to do this
            stack.pop(); 
        }
    }

    //Remove any remaining operators from the stack
    while(!stack.empty()) {
        postfix.Push_back(stack.top());
        stack.pop();
    }
}
2
Yuushi

L'implémentation C++ est donnée ci-dessous:


  void infix2postfix(string s)
   {
     stack<char>st;
     for(int i=0; i<s.length(); i++)
     {
        if(isdigit(s[i]) || isalpha(s[i]))  cout<<s[i];
        else if( s[i]==')' )
        {
           while(st.top()!='(')
           {
                cout<<st.top();
                st.pop();
           }
          st.pop();
        }

        else st.Push(s[i]);
      }
   }
0
rashedcs

Voici le mien en utilisant C avec une évaluation à plusieurs chiffres.

#include <stdio.h>
#include <math.h>
#define MAX 50
void Push(char[],char);
void in_Push(double[], double);
int pop();
int prec(char);
double eval(char[],int,double[]);
int top = 0;
void main() {
double eval_stack[MAX];
int op_count=0;
char stack[MAX], exps[MAX], symbols[MAX];
int i=0,j=0,len,check;
while((symbols[i]=getchar())!='\n') {
if(symbols[i]!=' ' || symbols[i]!='\t') {
if(symbols[i]=='+' || symbols[i]=='-' || symbols[i]=='/' || symbols[i]=='*' || symbols[i]=='^')
op_count++;
i++;
}
}
symbols[i]='#';
symbols[++i]='\0';
len = strlen(symbols);
stack[top] = '#';
for(i=0; i<=len; i++) {
if(symbols[i]>='a'  && symbols[i]<='z') {
exps[j]=symbols[i];
j++;
}
switch(symbols[i]) {
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
//if(symbols[i]>='a'  && symbols[i]<='z') {
exps[j]=symbols[i];
j++;
break;
case '+': case '-': case '*': case '/': case '^':
exps[j++] = ' ';
while(prec(symbols[i]) <= prec(stack[top])) {

             exps[j] = stack[top];
             pop();
             //printf("\n\t\t%d\t\t%d\n", top,j);
                j++;


}
if(prec(symbols[i]) > prec(stack[top])) {
Push(stack,symbols[i]);
}
break;
case '(':
Push(stack,symbols[i]);
break;
case ')':
while(stack[top]!='(') {
      exps[j] = stack[top];
       pop();
      j++;

}
pop();
break;
case '#':
exps[j++] = ' ';
while(stack[top]!='#') {
      exps[j] = stack[top];
       pop();
       j++;

}
pop();
break;
}
}
exps[j]='\0';
printf("Postfix: %s", exps);
for(i=0; i<j; i++)
if(exps[i]=='a')
check = 1;
if(check!=1)
printf("\nSolution: %.1f", eval(exps,j,eval_stack));
}

double eval(char exps[],int exps_len,double eval_stack[]) {
    int i; int len=exps_len,temp;
    double in_temp[MAX],t;
    int count,power,j,in_count;
    count=power=j=t=in_count=0;
    double result;
for(i=0; i<len; i++) {
switch(exps[i]) {
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
in_temp[i] = exps[i]-'0';
j=i+1;
while(exps[j]>='0' && exps[j]<='9') {
in_temp[j] = exps[j]-'0';
j++; // 2
}
count = i; // 3
while(in_temp[count]<='0' && in_temp[count]<='9') {
power = (j-count)-1;
t = t + in_temp[count]*(pow(10,power));
power--;
count++;
}
in_Push(eval_stack,t);
i=j-1;
t=0;
break;
case '+':
temp = pop();
pop();
result = eval_stack[temp] + eval_stack[temp+1];
in_Push(eval_stack,result);
break;
case '-':
temp = pop();
pop();
result = eval_stack[temp] - eval_stack[temp+1];
in_Push(eval_stack,result);
break;
case '*':
temp = pop();
pop();
result = eval_stack[temp] * eval_stack[temp+1];
in_Push(eval_stack,result);
break;
case '/':
temp = pop();
pop();
result = eval_stack[temp] / eval_stack[temp+1];
in_Push(eval_stack,result);
break;
case '^':
temp = pop();
pop();
result = pow(eval_stack[temp],eval_stack[temp+1]);
in_Push(eval_stack,result);
break;
}
}
return eval_stack[top];
}
int prec(char a) {
if(a=='^')
return 3;
else if(a=='*' || a=='/' || a=='%')
return 2;
else if(a=='+' || a=='-')
return 1;
else if(a=='(')
return 0;
else
return -1;
}

void Push(char stack[], char ele) {
    if(top>=MAX) {
    printf("\nStack Overflow");
    exit(1);
    }
stack[++top] = ele;
}
void in_Push(double stack[], double ele) {
    if(top>=MAX) {
    printf("\nStack Overflow");
    exit(1);
    }
stack[++top] = ele;
}
int pop() {
    if(top<0) {
    printf("\nStack Underflow");
    exit(1);
    }

top = top - 1;
return top;
}
0
rajatbarman