表达式求值

前两天翻看《数据结构》,看到有个表达式求值的东西比较有意思。于是乎就用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#   。以井号结尾

 

最后测试了 下自己写的还是有点小问题 ,火力还是不够啊  。 这是网上找的一个别人写的:

  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         }

 

posted @ 2015-04-14 00:29  assassinx  阅读(537)  评论(0编辑  收藏  举报