黑马程序员-控制台下的连续四则计算器

来源:互联网 发布:sd卡格式化后数据还在 编辑:程序博客网 时间:2024/06/03 02:21

------- Windows Phone 7手机开发、.Net培训、期待与您交流! -------

效果:输入一个四则运算式回车,计算这个算式的结果:


连续运算看似简单,且方法多样,但要做到有通用性和扩展性却很不容易,而且优先级的调整也让人头疼。

要点1:

后缀表达式:

假设用户输入了一个算式是这样的:
4+5-7*2
类似的长算式,怎么让程序连续计算呢,首先第一步是要把这个串调整成后缀表达式的形式,就是所有数字在前而运算符在后面倒序排列的形式,拿刚才那个算式来说,就是变成这样:
4,5,7,2,*,-,+

后缀表达式又称之为逆波兰算式,调整成后缀表达式的好处是操作数和操作符分开提取,当要增加运算种类和调整优先级的时候比较容易

下面是C#版的后缀表达式调整算法:

myStack stack1;            myStack NumStack;            initStack(out stack1);            initStack(out NumStack);            string str = Console.ReadLine();            //接受用户输入的一个串            string[] str2;            //声明一个储存数字的字符串数组            char[] op = new char[4] { '+', '-', '*', '/' };            //声明一个包含四种运算符的字符数组            //char[] opArray = new char[64];            //声明一个储存算式中出现的运算符(有顺序)的字符数组            str2 = str.Split(op);            //将输入串按照运算符的间隔分割成各个数字(需要转换成int)            for (int i = 0; i < str.Length; i++)            {//检索字符串中出现的运算符,倒序存入运算符数组中                switch (str.Substring(i, 1))                {                    case "+":                        pushStack(ref stack1, '+');                        break;                    case "-":                        pushStack(ref stack1, '-');                        break;                    case "*":                        pushStack(ref stack1, '*');                        break;                    case "/":                        pushStack(ref stack1, '/');                        break;                    default:                        break;                }            }            foreach (string s in str2)            {                pushStack(ref NumStack, s);            }

这里我们用到了栈结构,栈结构是FILO也就是先进后出了,当然你也可以使用泛型集合,把眼光从令人头疼的拆箱和装箱操作上移开,只关注程序的算法逻辑,贴一个笔者自己写的栈操作函数(很菜的水平):



如此,为了确保通用性而使用拆装箱操作而大大牺牲了效率,实在是得不偿失

如此,进行到这里我们已经把操作数和操作符分别压到了两个栈中,下面就是具体计算的过程:

运算的过程是这样的:
数字栈中弹出一个数
操作数栈弹出一个运算符
数字栈再弹出一个数
运算,将结果压回数字栈中
重复 弹出数字,弹出运算符 再弹出数字,运算,结果压入栈 这个操作

static int calc(ref myStack st, ref myStack NumStack)        {//接受两个栈的引用作为参数            int result=0;            int i=0;            while (NumStack.top > 0 && st.top>0)            {                result = Convert.ToInt32((string)popStack(ref NumStack));//从栈中弹出一个操作数(两次转换,object类型拆箱时只能拆成原来装进去的数据类型,也就是string,所以还要转换一次)                 switch ((char)popStack(ref st))                {//弹出一个操作符并判断操作符类型                     case '+':                        result += Convert.ToInt32((string)popStack(ref NumStack));                         pushStack(ref NumStack, result.ToString());//弹出操作数进行加法运算,将运算结果压回栈里面(想当初如果直接转换成int再压栈就不会这么转换来转换去了)                         break;                    case '-':                        result = Convert.ToInt32((string)popStack(ref NumStack))-result;                        pushStack(ref NumStack, result.ToString());//其余运算同加法                         break;                     case '*':                        result *= Convert.ToInt32((string)popStack(ref NumStack));                        pushStack(ref NumStack, result.ToString());                        break;                    case '/':                        try                        {                            result = Convert.ToInt32((string)popStack(ref NumStack))/result;                            pushStack(ref NumStack, result.ToString());                        }                        catch                        {                            Console.WriteLine("0不能做除数");                        }                        break;                     default:                        break;                 }            }                         return result;                    }

最后要解决的是优先级的问题,这个问题留给你们,因为操作符和操作数都在堆栈中,如果需要调整的运算符在栈底,那么全部弹出显然很不经济,这个时候就可以把栈看成数组或者队列来操作,我也没有什么太好的办法,而且同一优先级的运算符在一起还有结合性的问题,比如:

2-3+1=?

应该先算减法还是加法?同一级别的运算符的结合性应该是从左到右的,但是遇到括号就不行了

2-(3+1)=?

如果遇到++和--之类的单目运算符也很麻烦:

2+++3=?

究竟是2+(++3),还是2++(+3)(事实上,这是C语言语法中的一个陷阱)?

假设你要开发一个编译器的前端,那么确定运算符的优先级和结合性绝对是要首先考虑的问题,或许你也可以不用栈而用树来储存操作符合操作数,树结构中的元素比较容易调整位置。

------- Windows Phone 7手机开发、.Net培训、期待与您交流! -------

原创粉丝点击