資料結構中的表示式解析



表示式是任何單詞、單片語或符號,在求值時會生成一個值。表示式解析是指根據特定標準分析表示式的單詞或符號。表示式解析是程式語言中用於計算算術和邏輯表示式的術語。

編寫算術表示式的方式稱為表示法。算術表示式可以用三種不同但等效的表示法編寫,即不改變表示式的本質或輸出。這些表示法是:

  • 中綴表示法
  • 字首(波蘭)表示法
  • 字尾(逆波蘭)表示法

這些表示法以它們在表示式中使用運算子的方式命名。我們將在本章中學習這些內容。

中綴表示法

我們用中綴表示法編寫表示式,例如 a - b + c,其中運算子位於運算元之間。對於我們人類來說,用中綴表示法閱讀、書寫和表達很容易,但對於計算裝置來說並非如此。處理中綴表示法的演算法在時間和空間消耗方面可能很困難且代價高昂。

字首表示法

在這種表示法中,運算子位於運算元之前,即運算子寫在運算元前面。例如,+ab。這等效於其中綴表示法a + b。字首表示法也稱為波蘭表示法

字尾表示法

這種表示法風格稱為逆波蘭表示法。在這種表示法風格中,運算子位於運算元之後,即運算子寫在運算元之後。例如,ab+。這等效於其中綴表示法a + b

下表簡要地試圖展示三種表示法的區別:

序號 中綴表示法 字首表示法 字尾表示法
1 a + b + a b a b +
2 (a + b) ∗ c ∗ + a b c a b + c ∗
3 a ∗ (b + c) ∗ a + b c a b c + ∗
4 a / b + c / d + / a b / c d a b / c d / +
5 (a + b) ∗ (c + d) ∗ + a b + c d a b + c d + ∗
6 ((a + b) ∗ c) - d - ∗ + a b c d a b + c ∗ d -

表示式解析

正如我們所討論的,設計算法或程式來解析中綴表示法並不是一種非常有效的方法。相反,這些中綴表示法首先被轉換為字尾或字首表示法,然後計算。

要解析任何算術表示式,我們還需要注意運算子的優先順序和結合性。

優先順序

當一個運算元位於兩個不同的運算子之間時,哪個運算子將首先獲取運算元,這取決於一個運算子對其他運算子的優先順序。例如:

Operator Precendence

由於乘法運算優先於加法,因此將首先計算 b * c。稍後將提供運算子優先順序表。

結合性

結合性描述了在表示式中出現相同優先順序的運算子的規則。例如,在表示式 a + b - c 中,+ 和 - 具有相同的優先順序,那麼表示式中哪一部分將首先計算,這取決於這些運算子的結合性。在這裡,+ 和 - 都是左結合的,因此表示式將計算為(a + b) - c

優先順序和結合性決定了表示式的計算順序。以下是運算子優先順序和結合性表(從高到低):

序號 運算子 優先順序 結合性
1 指數 ^ 最高 右結合
2 乘法 (∗) & 除法 (/) 次高 左結合
3 加法 (+) & 減法 (-) 最低 左結合

上表顯示了運算子的預設行為。在表示式計算的任何時間點,都可以透過使用括號來更改順序。例如:

a + b*c中,表示式部分b*c將首先計算,因為乘法的優先順序高於加法。在這裡,我們使用括號來優先計算a + b,例如(a + b)*c

字尾表示式求值演算法

我們現在來看看如何計算字尾表示法的演算法:

Step 1. Scan the expression from left to right 
Step 2. If it is an operand push it to stack 
Step 3. If it is an operator pull operand from stack and 
        perform operation 
Step 4. Store the output of step 3, back to stack 
Step 5. Scan the expression until all operands are consumed 
Step 6. Pop the stack and perform operation

表示式解析 - 完整實現

以下是各種程式語言中表達式解析(從中綴表示法轉換為字尾表示法)的完整實現:

#include<stdio.h>
#include<string.h>
#include<ctype.h>
//char stack
char stack[25]; 
int top = -1; 
void push(char item) {
   stack[++top] = item; 
} 
char pop() {
   return stack[top--]; 
} 
//returns precedence of operators
int precedence(char symbol) {
   switch(symbol) {
      case '+': 
      case '-':
         return 2; 
         break; 
      case '*': 
      case '/':
         return 3; 
         break; 
      case '^': 
         return 4; 
         break; 
      case '(': 
      case ')': 
      case '#':
         return 1; 
         break; 
   } 
} 
//check whether the symbol is operator?
int isOperator(char symbol) {

   switch(symbol) {
      case '+': 
      case '-': 
      case '*': 
      case '/': 
      case '^': 
      case '(': 
      case ')':
         return 1; 
      break; 
         default:
         return 0; 
   } 
} 

//converts infix expression to postfix
void convert(char infix[],char postfix[]) {
   int i,symbol,j = 0; 
   stack[++top] = '#'; 
	
   for(i = 0;i<strlen(infix);i++) {
      symbol = infix[i]; 
		
      if(isOperator(symbol) == 0) {
         postfix[j] = symbol; 
         j++; 
      } else {
         if(symbol == '(') {
            push(symbol); 
         } else {
            if(symbol == ')') {
				
               while(stack[top] != '(') {
                  postfix[j] = pop(); 
                  j++; 
               } 
					
               pop();   //pop out (. 
            } else {
               if(precedence(symbol)>precedence(stack[top])) {
                  push(symbol); 
               } else {
					
                  while(precedence(symbol)<=precedence(stack[top])) {
                     postfix[j] = pop(); 
                     j++; 
                  } 
						
                  push(symbol); 
               }
            }
         }
      }
   }
	
   while(stack[top] != '#') {
      postfix[j] = pop(); 
      j++; 
   } 
	
   postfix[j]='\0';  //null terminate string. 
} 

//int stack
int stack_int[25]; 
int top_int = -1; 

void push_int(int item) {
   stack_int[++top_int] = item; 
} 

char pop_int() {
   return stack_int[top_int--]; 
} 

//evaluates postfix expression
int evaluate(char *postfix){

   char ch;
   int i = 0,operand1,operand2;

   while( (ch = postfix[i++]) != '\0') {
	
      if(isdigit(ch)) {
         push_int(ch-'0');  // Push the operand 
      } else {
         //Operator,pop two  operands 
         operand2 = pop_int();
         operand1 = pop_int();
			
         switch(ch) {
            case '+':
               push_int(operand1+operand2);
               break;
            case '-':
               push_int(operand1-operand2);
               break;
            case '*':
               push_int(operand1*operand2);
               break;
            case '/':
               push_int(operand1/operand2);
               break;
         }
      }
   }
	
   return stack_int[top_int];
}

void main() { 
   char infix[25] = "1*(2+3)",postfix[25]; 
   convert(infix,postfix); 
	
   printf("Infix expression is: %s\n" , infix);
   printf("Postfix expression is: %s\n" , postfix);
   printf("Evaluated expression is: %d\n" , evaluate(postfix));
}

輸出

Infix expression is: 1*(2+3)
Postfix expression is: 123+*
Evaluated expression is: 5 
// C++ Code for Expression Parsing Using Stack
#include <iostream>
#include <string>
#include <cctype>
#include <stack>
// char stack
std::stack<char> stack;

void push(char item) {
    stack.push(item);
}
char pop() {
    char top = stack.top();
    stack.pop();
    return top;
}
// returns precedence of operators
int precedence(char symbol) {
    switch(symbol) {
        case '+':
        case '-':
            return 2;
        case '*':
        case '/':
            return 3;
        case '^':
            return 4;
        case '(':
        case ')':
        case '#':
            return 1;
    }
    return 0;
}
// check whether the symbol is an operator
int isOperator(char symbol) {
    switch(symbol) {
        case '+':
        case '-':
        case '*':
        case '/':
        case '^':
        case '(':
        case ')':
            return 1;
        default:
            return 0;
    }
}
// converts infix expression to postfix
void convert(const std::string& infix, std::string& postfix) {
    int j = 0;
    stack.push('#');
    for (char symbol : infix) {
        if (isOperator(symbol) == 0) {
            postfix += symbol;
            j++;
        } else {
            if (symbol == '(') {
                push(symbol);
            } else {
                if (symbol == ')') {
                    while (stack.top() != '(') {
                        postfix += pop();
                        j++;
                    }
                    stack.pop(); // pop out '('
                } else {
                    if (precedence(symbol) > precedence(stack.top())) {
                        push(symbol);
                    } else {
                        while (precedence(symbol) <= precedence(stack.top())) {
                            postfix += pop();
                            j++;
                        }
                        push(symbol);
                    }
                }
            }
        }
    }

    while (stack.top() != '#') {
        postfix += pop();
        j++;
    }

    postfix[j] = '\0'; // null terminate string
}
// evaluates postfix expression
int evaluate(const std::string& postfix) {
    std::stack<int> stack_int;
    int operand1, operand2;
    for (char ch : postfix) {
        if (std::isdigit(ch)) {
            stack_int.push(ch - '0'); // Push the operand
        } else {
            // Operator, pop two operands
            operand2 = stack_int.top();
            stack_int.pop();
            operand1 = stack_int.top();
            stack_int.pop();
            switch (ch) {
                case '+':
                    stack_int.push(operand1 + operand2);
                    break;
                case '-':
                    stack_int.push(operand1 - operand2);
                    break;
                case '*':
                    stack_int.push(operand1 * operand2);
                    break;
                case '/':
                    stack_int.push(operand1 / operand2);
                    break;
            }
        }
    }
    return stack_int.top();
}
int main() {
    std::string infix = "1*(2+3)", postfix;
    convert(infix, postfix);
    std::cout << "Infix expression is: " << infix << std::endl;
    std::cout << "Postfix expression is: " << postfix << std::endl;
    std::cout << "Evaluated expression is: " << evaluate(postfix) << std::endl;
    return 0;
}

輸出

Infix expression is: 1*(2+3)
Postfix expression is: 123+*
Evaluated expression is: 5
// Java Code for Expression Parsing Using Stack
import java.util.Stack;
public class Main {
    // char stack
    static Stack<Character> stack = new Stack<>();
    static void push(char item) {
        stack.push(item);
    }
    static char pop() {
        return stack.pop();
    }
    // returns precedence of operators
    static int precedence(char symbol) {
        switch (symbol) {
            case '+':
            case '-':
                return 2;
            case '*':
            case '/':
                return 3;
            case '^':
                return 4;
            case '(':
            case ')':
            case '#':
                return 1;
        }
        return 0;
    }
    // check whether the symbol is an operator
    static int isOperator(char symbol) {
        switch (symbol) {
            case '+':
            case '-':
            case '*':
            case '/':
            case '^':
            case '(':
            case ')':
                return 1;
            default:
                return 0;
        }
    }
    // converts infix expression to postfix
    static void convert(String infix, StringBuilder postfix) {
        int j = 0;
        stack.push('#');
        for (char symbol : infix.toCharArray()) {
            if (isOperator(symbol) == 0) {
                postfix.append(symbol);
                j++;
            } else {
                if (symbol == '(') {
                    push(symbol);
                } else {
                    if (symbol == ')') {
                        while (stack.peek() != '(') {
                            postfix.append(pop());
                            j++;
                        }
                        stack.pop(); // pop out '('
                    } else {
                        if (precedence(symbol) > precedence(stack.peek())) {
                            push(symbol);
                        } else {
                            while (precedence(symbol) <= precedence(stack.peek())) {
                                postfix.append(pop());
                                j++;
                            }
                            push(symbol);
                        }
                    }
                }
            }
        }
        while (stack.peek() != '#') {
            postfix.append(pop());
            j++;
        }
    }
    // evaluates postfix expression
    static int evaluate(String postfix) {
        Stack<Integer> stackInt = new Stack<>();
        int operand1, operand2;
        for (char ch : postfix.toCharArray()) {
            if (Character.isDigit(ch)) {
                stackInt.push(ch - '0'); // Push the operand
            } else {
                // Operator, pop two operands
                operand2 = stackInt.pop();
                operand1 = stackInt.pop();
                switch (ch) {
                    case '+':
                        stackInt.push(operand1 + operand2);
                        break;
                    case '-':
                        stackInt.push(operand1 - operand2);
                        break;
                    case '*':
                        stackInt.push(operand1 * operand2);
                        break;
                    case '/':
                        stackInt.push(operand1 / operand2);
                        break;
                }
            }
        }
        return stackInt.peek();
    }
    public static void main(String[] args) {
        String infix = "1*(2+3)";
        StringBuilder postfix = new StringBuilder();
        convert(infix, postfix);
        System.out.println("Infix expression is: " + infix);
        System.out.println("Postfix expression is: " + postfix);
        System.out.println("Evaluated expression is: " + evaluate(postfix.toString()));
    }
}

輸出

Infix expression is: 1*(2+3)
Postfix expression is: 123+*
Evaluated expression is: 5
class Main:
    stack = []
    @staticmethod
    def push(item):
        Main.stack.append(item)
    @staticmethod
    def pop():
        return Main.stack.pop()
    #returns precedence of operators
    @staticmethod
    def precedence(symbol):
        if symbol in ['+', '-']:
            return 2
        elif symbol in ['*', '/']:
            return 3
        elif symbol == '^':
            return 4
        elif symbol in ['(', ')', '#']:
            return 1
        return 0
    #check whether the symbol is an operator
    @staticmethod
    def is_operator(symbol):
        return symbol in ['+', '-', '*', '/', '^', '(', ')']
    @staticmethod
    def convert(infix):
        postfix = ""
        j = 0
        Main.push('#')
        for symbol in infix:
            if not Main.is_operator(symbol):
                postfix += symbol
                j += 1
            else:
                if symbol == '(':
                    Main.push(symbol)
                else:
                    if symbol == ')':
                        while Main.stack[-1] != '(':
                            postfix += Main.pop()
                            j += 1
                        Main.pop()  # pop out '('
                    else:
                        if Main.precedence(symbol) > Main.precedence(Main.stack[-1]):
                            Main.push(symbol)
                        else:
                            while Main.precedence(symbol) <= Main.precedence(Main.stack[-1]):
                                postfix += Main.pop()
                                j += 1
                            Main.push(symbol)
        while Main.stack[-1] != '#':
            postfix += Main.pop()
            j += 1
        return postfix
    @staticmethod
    def evaluate(postfix):
        stack_int = []
        for ch in postfix:
            if ch.isdigit():
                stack_int.append(int(ch))
            else:
                operand2 = stack_int.pop()
                operand1 = stack_int.pop()
                if ch == '+':
                    stack_int.append(operand1 + operand2)
                elif ch == '-':
                    stack_int.append(operand1 - operand2)
                elif ch == '*':
                    stack_int.append(operand1 * operand2)
                elif ch == '/':
                    stack_int.append(operand1 / operand2)
        return stack_int[0]
    @staticmethod
    def main():
        infix = "1*(2+3)"
        postfix = Main.convert(infix)
        print("Infix expression is:", infix)
        print("Postfix expression is:", postfix)
        print("Evaluated expression is:", Main.evaluate(postfix))
Main.main()

輸出

Infix expression is: 1*(2+3)
Postfix expression is: 123+*
Evaluated expression is: 5

使用棧進行表示式解析

我們可以使用不同的資料結構來實現表示式解析。檢視使用棧進行表示式解析的實現

廣告