先来个运行截图吧:
老师要求用c#写个计算器,虽然写了无数遍了(用c写,c++写,java写...),于是这次决定用逆波兰撸个新鲜的计算器,和原先我那个java用递归方法写的相比,这里实在是让代码简洁了许多许多,而且浮点运算,正负号都可以支持,果然还是体现了数据结构的美丽和强大
先写两个栈:
namespace Utils_Calculate { public class Static_Double { private double[] opnd = new double[500]; private int cur = -1; public void Push(double p){ if (cur < 500) opnd[++cur] = p; } public bool IsEmpty(){ return cur < 0 ? true : false; } public double Pop() { if (cur >= 0) return opnd[cur--]; else return 0; } public void clear(){ cur = -1; } } public class Static_Char { private char[] optr = new char[500]; private int cur = -1; public void Push(char c){ if (cur < 500) optr[++cur] = c; } public bool IsEmpty(){ return cur < 0 ? true : false; } public char Pop(){ if (cur >= 0) return optr[cur--]; else return '\n'; } public void clear(){ cur = -1; } } }
具体代码如下:
namespace Utils_Calculate { class CharUtil { public static bool IsDigit(char c){ return (c >= 48 && c <= 57 || c == '.') ? true : false; } } public class Rpn_Calculate: IRpn_Calculate { //定义优先级,即当前所指运算符优先级为0,栈顶元素优先级为-1 private int cur_pri = 0, top_pri = -1; //数据栈 Static_Double opnd = new Static_Double(); //符号栈 Static_Char optr = new Static_Char(); //优先级定义 private int GetPRI(char c){ if (c == '+' || c == '-') return 1; if (c == '*' || c == '/') return 2; if (c == '(' || c == ')') return 0; return -1; } //计算函数 private double Cal(double before,char mark,double after){ if (mark == '+') return before + after; if (mark == '-') return before - after; if (mark == '*') return before * after; if (mark == '/') return before / after; throw new System.Exception(); } //逆波兰计算 public double Rnp(char[] str,int len){ try { for (int i = 0; i < len; i++){ string x = "";double read_x = 0;char c = ' '; //如果c是数字或小数点 if (CharUtil.IsDigit(c=str[i])){ x += c; //循环读数包括小数点(比如8.88+7.777,这里会分三次读,第一次循环读入8.88,第二次读入+,第三次循环读入7.777) while (i < len-1&& CharUtil.IsDigit(c = str[++i])) x += c; read_x = double.Parse(x); //读出的数据入数据栈 opnd.Push(read_x); } //如果c是运算符,并且c的优先级高于栈顶符号的优先级,则c入栈 if ((cur_pri = GetPRI(c)) >= 0) { if (c=='('|| cur_pri > top_pri){ top_pri = cur_pri; optr.Push(c); //如果c是')',则符号栈的元素不断出桟、运算,直到遇到左括号 }else if (c == ')'){ char _c =' '; double result = 0; while ((_c=optr.Pop())!='('){ double after = opnd.Pop(); result = opnd.Pop(); result = Cal(result, _c,after); opnd.Push(result); } //栈顶优先级更新 if (optr.IsEmpty())top_pri = -1; else{ char char_top = optr.Pop(); top_pri = GetPRI(char_top); optr.Push(char_top); } }else{ //当前运算符优先级低于栈顶,先出栈计算(运算符出桟一个,数据栈出桟两个),再将当前运算符入栈 opnd.Push(Stack_Cal(optr.Pop())); optr.Push(c); top_pri = GetPRI(c); } } } double final_result = 0; //最后结束的时候两个栈里面的优先级是有序的,需要出桟计算一波 while (!optr.IsEmpty()){ char _c = optr.Pop(); double after = opnd.Pop(); final_result = opnd.Pop(); final_result = Cal(final_result, _c, after); opnd.Push(final_result); } //返回结果 double _result=opnd.Pop(); Reset(); return _result; } catch (System.Exception){ throw new System.Exception("请不要乱输谢谢.."); } } //还原配置 private void Reset() { cur_pri = 0; top_pri = -1; opnd.clear();optr.clear(); } //出栈计算(运算符出桟一个,数据栈出桟两个) private double Stack_Cal(char mark){ double after = opnd.Pop(); double before = opnd.Pop(); return Cal(before, mark, after); } } }
class Program { static void Main(string[] args) { //测试代码: Rpn_Calculate calculator = new Rpn_Calculate(); const string test = "1+2*(3-4)/5.5"; double result = calculator.Rnp(test.ToCharArray(), test.Length); Console.WriteLine(result); Console.ReadKey(); } }
做个UI界面也是个不错的想法: