今天做个带窗口的C#四则运算计算器
输入中缀表达式(自然表达式) 可以用list来放
先把它变成后缀表达式(逆波兰表达式)
用一个栈放运算符,另一个栈放后缀表达式
运算符优先级:
1:(
2:+ -
3:* /
4:)
从左到右遍历中缀表达式
-
遇到数字,后缀表达式进栈
-
遇到运算符:先判断栈是否为空。若是,则直接将此运算符压入栈。若不是,则查看当前栈顶元素。若栈顶元素优先级大于或等于此操作符级别,则弹出栈顶元素,将栈顶元素添加到后缀表达式中,并继续进行上述判断。如果不满足上述判断或者栈为空,将这个运算符入栈。
-
遇到括号:如果是左括号,直接入栈。如果是右括号,弹出栈中第一个左括号后边所有的操作符,并将左括号弹出。(右括号不用入栈)
-
字符串遍历结束后,如果栈不为空,则弹出栈中所有元素,将它们添加到后缀表达式的末尾,直到栈为空。
计算后缀表达式:
从左到右扫描后缀表达式,如果是数字,放入数字栈。如果是符号,从数字栈中弹出两个数字,第一个取出的数字为右运算数,第二个为左运算数,进行运算。然后将结果放进数字栈中。如此反复,直到读完整个表达式后,留在数字栈中的那个数字就是最终结果。
一部分代码
List<char> exp;
#region
获取表达式
#endregion
//获取运算符优先级的函数//
private int getPriority(char ch)
{
//获取优先级
if (ch == '(') return 1;
else if (ch == '+' || ch == '-') return 2;
else if (ch == '*' || ch == '/') return 3;
else return 4;
}
/按等号开始计算//
private void button17_Click(object sender, EventArgs e)
{
#region 将中缀表达式改为后缀表达式
Stack<char> houzhui = new Stack<char>();
Stack<char> yunsuanfu= new Stack<char>();
for (int i = 0; i < exp.Count; i++)
{
//数字
if (exp[i] >= '0' && exp[i] <= '9')
{
houzhui.Push(exp[i]);
}
//运算符
else if(exp[i] == '+' || exp[i] == '-' || exp[i] == '*' || exp[i] == '/')
{
if (yunsuanfu.Count == 0)
{
yunsuanfu.Push(exp[i]);
}
else
{
while(yunsuanfu.Count != 0)
{
if(getPriority(yunsuanfu.Peek()) >= getPriority(exp[i])) {
houzhui.Push(yunsuanfu.Pop());
}
else break;
}
yunsuanfu.Push(exp[i]);
}
}
//括号
else
{
if (exp[i] == '(')
{
yunsuanfu.Push(exp[i]);
}
else
{
while (yunsuanfu.Peek() != '(')
{
houzhui.Push(yunsuanfu.Pop());
}
yunsuanfu.Pop();
}
}
}
while (yunsuanfu.Count != 0)
{
houzhui.Push(yunsuanfu.Pop());
}
//把栈中的后缀表达式挪到一个数组中
char[] hz = new char[houzhui.Count];
int length = houzhui.Count;
while (houzhui.Count > 0)
{
hz[houzhui.Count-1] = houzhui.Pop();
}
#endregion
#region 计算后缀表达式
Stack<double> num = new Stack<double>();
double num1, num2, num3 = 0;
for(int i = 0; i < length ; i++)
{
//数字
if (hz[i] >= '0' && hz[i] <= '9')
{
num.Push(Convert.ToDouble(hz[i].ToString()));
}
//符号
else
{
num2 = num.Pop();
num1 = num.Pop();
if (hz[i] == '+')
{
num3 = num1 + num2;
}
else if (hz[i] == '-')
{
num3 = num1 - num2;
}
else if (hz[i] == '*')
{
num3 = num1 * num2;
}
else if (hz[i] == '/')
{
num3 = num1 / num2;
}
num.Push(num3);
}
}
double result = num.Pop();
textBox1.Text = (Convert.ToString(result));
#endregion
}
Some notes today
什么时候会发生装箱和拆箱
调用含有object类型的方法的时候,传入值类型的时候就会发生装箱,方法之中处理该参数的时候会发生拆箱。
定义泛型的时候,如果为了通用定义object类型,当添加值类型的时候就会发生装箱和拆箱。
值类型的实例通常是在线程栈上分配的(静态分配),但是在某些情形下可以存储在堆中。引用类型的对象总是在进程堆中分配(动态分配)。
ArrayList中的项是Object类型,List是泛型(要在声明时声明集合内数据对象类型)
C# out参数在函数内部要赋值。如果是out参数数组的话,用for循环赋值会报错。for循环是不一定进入的。