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

------- 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培训、期待与您交流! -------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值