前兩天翻看《數據結構》,看到有個表達式求值的東西比較有意思。因而乎就用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 }