前兩天翻看《數據結構》,看到有個表達式求值的東西比較有意思。於是乎就用c#代碼實現了下。倒騰了半天 總算能工作了。 看到博客園的前輩們也寫過好多類似的例子 獻丑了。程序設計語言中都有計算表達式的問題,這是語言編譯中的典型問題。看到博客園的其他帖子好多都是說什麼後綴表達式 什麼的。我這個代碼比較短 但是基礎功能是完全實現了的。
《數據結構》第3章63 頁 是講堆棧。就是stack 這個鳥玩意兒 。以前存數據 都是用list 類似於鏈表 。誰用過這個啊 有什麼用。沒什麼用 就是一種數據結構。表達式求值這當中利用了一個重要特性 那就是堆棧的先進後出。 不論是我們的高級程序語言 還是表達式 ,他們都有一個重要的特性就是 大括號包小括號 括號當中包括號。並且有左括號就會有右括號 是相互對稱的 ,其實要說的話 這個是一種遞歸結構。 不管怎麼說 遇左括號入棧 遇右括號出棧 ,堆棧天生就有一種處理這種問題的能力,並且還讓條理更清晰。
我這段代碼的大概原理就是書上講的那樣:
先給個運算符優先級表
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若優先級相同 說明左右括號相遇 只需將棧頂運算符退棧即可
當棧頂操作符和剛讀入操作符均為#時 說明表達式結束
就這樣如此往復 遇到一個小的計算單位就會自動求值並消除操作數和操作符 然後又讓求出的值和其他值進行運算 如此往復以小到大都求出整個表達式的值。
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# 。以井號結尾