前两天翻看《数据结构》,看到有个表达式求值的东西比较有意思。于是乎就用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栈 遇caozuofu.html" target="_blank">操作符进行下面的逻辑
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# 。以井号结尾