表達式求值

前兩天翻看《數據結構》,看到有個表達式求值的東西比較有意思。因而乎就用c#代碼實現了下。倒騰了半天 總算能工做了。 看到博客園的前輩們也寫過好多相似的例子 獻醜了。程序設計語言中都有計算表達式的問題,這是語言編譯中的典型問題。看到博客園的其餘帖子好多都是說什麼後綴表達式 什麼的。我這個代碼比較短 可是基礎功能是徹底實現了的。c#

《數據結構》第3章63 頁 是講堆棧。就是stack 這個鳥玩意兒 。之前存數據 都是用list 相似於鏈表 。誰用過這個啊 有什麼用。沒什麼用 就是一種數據結構。表達式求值這當中利用了一個重要特性 那就是堆棧的先進後出。 不管是咱們的高級程序語言 仍是表達式  ,他們都有一個重要的特性就是 大括號包小括號  括號當中包括號。而且有左括號就會有右括號 是相互對稱的 ,其實要說的話 這個是一種遞歸結構。 無論怎麼說  遇左括號入棧  遇右括號出棧  ,堆棧天生就有一種處理這種問題的能力,而且還讓條理更清晰。數據結構

我這段代碼的大概原理就是書上講的那樣:測試

先給個運算符優先級表spa

1 char[,] compareTable = new char[7, 7]{
2             {'>','>','<','<','<','>','>'},
3             {'>','>','<','<','<','>','>'},
4             {'>','>','>','>','<','>','>'},
5             {'>','>','>','>','<','>','>'},
6             {'<','<','<','<','<','=','x'},
7             {'>','>','>','>','x','>','>'},
8             {'<','<','<','<','<','x','='}
9             };

橫着豎着7行7列  依次對應+-*/()#   行數爲第一個變量 列數爲第二個變量 ,好比+與* 爲 compare[0,2] 是小於符號 ,+號優先級小於*
而後是規則:
從左往右掃描,遇操做數進operator棧 遇操做符進行下面的邏輯
1若棧頂運算符優先級低於剛讀入的運算符 則讓剛讀入的運算符進入operator棧
2若棧頂運算符的優先級高於剛讀入的運算符則將棧頂運算符退棧 ,同時將操做數棧operand退棧兩次 讓他們進行運算 運算結果推入operator棧
3若優先級相同 說明左右括號相遇 只需將棧頂運算符退棧便可
當棧頂操做符和剛讀入操做符均爲#時 說明表達式結束設計

就這樣如此往復 遇到一個小的計算單位就會自動求值並消除操做數和操做符  而後又讓求出的值和其餘值進行運算 如此往復以小到大都求出整個表達式的值。code

  1 public char compareOper(char opr1, char opr2)
  2         {
  3             char[,] compareTable = new char[7, 7]{
  4             {'>','>','<','<','<','>','>'},
  5             {'>','>','<','<','<','>','>'},
  6             {'>','>','>','>','<','>','>'},
  7             {'>','>','>','>','<','>','>'},
  8             {'<','<','<','<','<','=','x'},
  9             {'>','>','>','>','x','>','>'},
 10             {'<','<','<','<','<','x','='}
 11             };
 12 
 13             int opr1Indx = 0;
 14             switch (opr1)
 15             {
 16                 case '+':
 17                     opr1Indx = 0;
 18                     break;
 19                 case '-':
 20                     opr1Indx = 1;
 21                     break;
 22                 case '*':
 23                     opr1Indx = 2;
 24                     break;
 25                 case '/':
 26                     opr1Indx = 3;
 27                     break;
 28                 case '(':
 29                     opr1Indx = 4;
 30                     break;
 31                 case ')':
 32                     opr1Indx = 5;
 33                     break;
 34                 case '#':
 35                     opr1Indx = 6;
 36                     break;
 37                 default:
 38                     break;
 39             }
 40             int opr2Indx = 0;
 41             switch (opr2)
 42             {
 43                 case '+':
 44                     opr2Indx = 0;
 45                     break;
 46                 case '-':
 47                     opr2Indx = 1;
 48                     break;
 49                 case '*':
 50                     opr2Indx = 2;
 51                     break;
 52                 case '/':
 53                     opr2Indx = 3;
 54                     break;
 55                 case '(':
 56                     opr2Indx = 4;
 57                     break;
 58                 case ')':
 59                     opr2Indx = 5;
 60                     break;
 61                 case '#':
 62                     opr2Indx = 6;
 63                     break;
 64                 default:
 65                     break;
 66             }
 67 
 68             return compareTable[opr1Indx, opr2Indx];
 69         }
 70         public int calc(int num1, int num2, char opr)
 71         {
 72             switch (opr)
 73             {
 74                 case '+':
 75                     return num1 + num2;
 76                     break;
 77                 case '-':
 78                     return num1 - num2;
 79                     break;
 80                 case '*':
 81                     return num1 * num2;
 82                     break;
 83                 case '/':
 84                     return num1 / num2;
 85                     break;
 86 
 87                 default:
 88                     break;
 89             }
 90             return 0;
 91         }
 92         private void button1_Click(object sender, EventArgs e)
 93         {
 94             Stack<int> operand = new Stack<int>();
 95             Stack<char> opera = new Stack<char>();
 96             opera.Push('#');
 97 
 98             string exp = textBox1.Text;
 99 
100             Regex numVali = new Regex(@"\d");
101 
102             //MessageBox.Show(numVali.IsMatch("6").ToString());
103 
104             for (int i = 0; i < exp.Length; i++)
105             {
106                 if (numVali.IsMatch(exp[i] + "") == true || exp[i] == ' ')//數字
107                 {
108                     string numTmp = exp[i] + "";
109                     int nextNumIndx = 1;
110                     char nextNum = exp[i + (nextNumIndx)];
111                     nextNumIndx++;
112                     while (numVali.IsMatch(nextNum + "") == true || nextNum == ' ')
113                     {
114                         numTmp += nextNum;
115                         if (i + (nextNumIndx) < exp.Length)
116                         {
117                             nextNum = exp[i + (nextNumIndx)];
118                             nextNumIndx++;
119                         }
120                         else
121                             break;
122                     }
123                     operand.Push(int.Parse(numTmp.Replace(" ", "")));
124                     i = i + nextNumIndx - 1 - 1;
125 
126                 }
127                 else//操做符
128                 {
129 
132                     switch (compareOper(opera.Peek(), exp[i]))
133                     {
134                         case '<':
135                             opera.Push(exp[i]);
136                             break;
137                         case '=':
138                             opera.Pop();
139                             break;
140                         case '>':
141 
142                             int b = operand.Pop();
143                             int a = operand.Pop();
144                             operand.Push(calc(a, b, opera.Pop()));
145 
146  
147                             if (compareOper(opera.Peek(), exp[i]) == '=')
148                             {
149                                 opera.Pop();
150                             }
151                             else if (compareOper(opera.Peek(), exp[i]) == '>')
152                             {
153                                  b = operand.Pop();
154                                  a = operand.Pop();
155                                 operand.Push(calc(a, b, opera.Pop()));
156                                 //opera.Push(exp[i]);
157 
158                                 if (compareOper(opera.Peek(), exp[i]) == '=')
159                                 {
160                                     opera.Pop();
161                                 }
162                                 else if (compareOper(opera.Peek(), exp[i]) == '>')
163                                 {
164                                     b = operand.Pop();
165                                     a = operand.Pop();
166                                     operand.Push(calc(a, b, opera.Pop()));
167                                     opera.Push(exp[i]);
168                                 }
169                                 else if (compareOper(opera.Peek(), exp[i]) == '<')
170                                     opera.Push(exp[i]);
171                             }
172                             else if (compareOper(opera.Peek(), exp[i]) == '<')
173                                 opera.Push(exp[i]);
174                             break;
175                         default:
176                             break;
177                     }
178                     if (exp[i] == '#')
179                         break;
180                 }
181             }
182             MessageBox.Show(string.Format("結果是{0}", operand.Peek()));
183         }


就這樣了 ,親測 可用。暫時只能計算整數哈,一個正確的輸入 應該像這樣:(3+2)*4#   。以井號結尾orm

 

最後測試了 下本身寫的仍是有點小問題 ,火力仍是不夠啊  。 這是網上找的一個別人寫的:blog

  1 public void calcM(string s)
  2         {
  3             int kuohaoCount=0;
  4             foreach (var item in s)
  5             {
  6                 if (item == '(' || item == ')')
  7                     kuohaoCount++;
  8             }
  9             if (kuohaoCount % 2 != 0)
 10             {
 11                 MessageBox.Show("公式裏括號個數不合法");
 12                 return;
 13             }
 14             else if(s.IndexOf('=')>=0)
 15             {
 16                 MessageBox.Show("公式裏不能出現等號");
 17                 return;
 18             }
 19 
 20             s = s.ToUpper();
 21             s=s.Replace('A', '1');
 22             s = s.Replace('B', '1');
 23             s = s.Replace('C', '1');
 24 
 25             try
 26             {
 27                 string S = ""; //後綴
 28                 char[] Operators = new char[s.Length];
 29                 int Top = -1;
 30                 for (int i = 0; i < s.Length; i++)
 31                 {
 32                     char C = s[i];
 33                     switch (C)
 34                     {
 35                         case ' ': //忽略空格
 36                             break;
 37                         case '+': //操做符
 38                         case '-':
 39                             while (Top >= 0) //棧不爲空時
 40                             {
 41                                 char c = Operators[Top--]; //pop Operator
 42                                 if (c == '(')
 43                                 {
 44                                     Operators[++Top] = c; //push Operator
 45                                     break;
 46                                 }
 47                                 else
 48                                 {
 49                                     S = S + c;
 50                                 }
 51                             }
 52                             Operators[++Top] = C; //push Operator
 53                             S += " ";
 54                             break;
 55                         case '*': //忽略空格
 56                         case '/':
 57                             while (Top >= 0) //棧不爲空時
 58                             {
 59                                 char c = Operators[Top--]; //pop Operator
 60                                 if (c == '(')
 61                                 {
 62                                     Operators[++Top] = c; //push Operator
 63                                     break;
 64                                 }
 65                                 else
 66                                 {
 67                                     if (c == '+' || c == '-')
 68                                     {
 69                                         Operators[++Top] = c; //push Operator
 70                                         break;
 71                                     }
 72                                     else
 73                                     {
 74                                         S = S + c;
 75                                     }
 76                                 }
 77                             }
 78                             Operators[++Top] = C; //push Operator
 79                             S += " ";
 80                             break;
 81                         case '(':
 82                             Operators[++Top] = C;
 83                             S += " ";
 84                             break;
 85                         case ')':
 86                             while (Top >= 0) //棧不爲空時
 87                             {
 88                                 char c = Operators[Top--]; //pop Operator
 89                                 if (c == '(')
 90                                 {
 91                                     break;
 92                                 }
 93                                 else
 94                                 {
 95                                     S = S + c;
 96                                 }
 97                             }
 98                             S += " ";
 99                             break;
100                         default:
101                             S = S + C;
102                             break;
103 
104                     }
105                 }
106                 while (Top >= 0)
107                 {
108                     S = S + Operators[Top--]; //pop Operator
109                 }
110 
111                 //System.Console.WriteLine(S); //後綴
112 
113                 //後綴表達式計算
114                 double[] Operands = new double[S.Length];
115                 double x, y, v;
116                 Top = -1;
117                 string Operand = "";
118                 for (int i = 0; i < S.Length; i++)
119                 {
120                     char c = S[i];
121                     if ((c >= '0' && c <= '9') || c == '.')
122                     {
123                         Operand += c;
124                     }
125 
126                     if ((c == ' ' || i == S.Length - 1) && Operand != "") //Update
127                     {
128                         Operands[++Top] = System.Convert.ToDouble(Operand); //push Operands
129                         Operand = "";
130                     }
131 
132                     if (c == '+' || c == '-' || c == '*' || c == '/')
133                     {
134                         if ((Operand != ""))
135                         {
136                             Operands[++Top] = System.Convert.ToDouble(Operand); //push Operands
137                             Operand = "";
138                         }
139                         y = Operands[Top--]; //pop 雙目運算符的第二操做數 (後進先出)注意操做數順序對除法的影響
140                         x = Operands[Top--]; //pop 雙目運算符的第一操做數
141                         switch (c)
142                         {
143                             case '+':
144                                 v = x + y;
145                                 break;
146                             case '-':
147                                 v = x - y;
148                                 break;
149                             case '*':
150                                 v = x * y;
151                                 break;
152                             case '/':
153                                 v = x / y; // 第一操做數 / 第二操做數 注意操做數順序對除法的影響
154                                 break;
155                             default:
156                                 v = 0;
157                                 break;
158                         }
159                         Operands[++Top] = v; //push 中間結果再次入棧
160                     }
161                 }
162                 v = Operands[Top--]; //pop 最終結果
163                 MessageBox.Show("結果是:" + v);
164             }
165             catch (System.Exception ex)
166             {
167                 MessageBox.Show("公式解析出現錯誤");
168                
169             }
170             
171         }
相關文章
相關標籤/搜索