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