通过这周学校的综合实验,自己写的程序终于从以前的黑框框变成了一个真正能让人看的程序。由于有一定的基础,所以并不会感到非常的难,以下是我遇到的一些相对比较棘手的问题。
一、使用栈实现计算器优先级
计算器四则运算优先级老师要用栈去实现,就去翻了翻数据结构书,思路大概如下:
所包含的运算符有‘+’,‘-’,‘*’,‘/’, 表达式为字符串。
(1)建立两个栈,一个用来存储操作数,另一个用来存储运算符, 开始时在运算符栈中先压入‘/0’,表达式的结束符。
(2)然后从左至右依次读取表达式中的各个符号(操作数或者运算符);
(3)如果读到的是操作数直接存入操作数栈;
(4)如果读到的是运算符,则作进一步判断:
若读到的运算符的优先级等于或小于之前的运算符的优先级,则从操作数中退出2个,从运算符中退出一个进行运算,将运算结果存入操作数栈;再把之前读到的运算符与目前的运算符栈顶比较,重复步骤(4)(即现在不读下一个元素);
若读到的是‘/0’结束符,而且此时运算符栈的栈顶元素也是‘/0’结束符,则运算结束,输出操作数栈中的元素即为最后结果。
// 进行运算
private double Calculate(string operators, string numbers) {
// 将数字和操作符字符串存到数组当中
string[] numberarray = Regex.Split(numbers, " ");
string[] operatorarray = Regex.Split(operators, " ");
Stack numberStack = new Stack();// 数字栈
Stack operatorStack = new Stack();// 操作符栈
// 先将前两个数字和一个操作符push到栈中
numberStack.Push(Convert.ToDouble(numberarray[0]));
numberStack.Push(Convert.ToDouble(numberarray[1]));
operatorStack.Push(operatorarray[0]);
// 当遍历完操作符数组且栈中操作符为空时跳出循环
for (int i = 1; i < operatorarray.Length || !operatorStack.IsEmpty();) {
// 当操作符栈为空时,push一个数字和一个操作符
if (operatorStack.IsEmpty()) {
numberStack.Push(Convert.ToDouble(numberarray[i + 1]));
operatorStack.Push(operatorarray[i]);
i++;continue;
}
// 当栈顶操作符为+或者-时
if (operatorStack.getTop().Equals("+") || operatorStack.getTop().Equals("-")) {
if (i < operatorarray.Length) { // 当下一个操作符不是最后一个操作符时
// 如果下一个操作符为*或者/,则push一个数字和操作符
if (operatorarray[i].Equals("*") || operatorarray[i].Equals("/")) {
numberStack.Push(Convert.ToDouble(numberarray[i + 1]));
operatorStack.Push(operatorarray[i]);
i++;
} else {// 否则,栈内进行加减运算,将结果push到栈顶,下同
double after = Convert.ToDouble(numberStack.Pop());
double pre = Convert.ToDouble(numberStack.Pop());
string op = operatorStack.Pop().ToString();
switch (op) {
case "+": { numberStack.Push(pre + after); continue; }
case "-": { numberStack.Push(pre - after); continue; }
default: continue;
}
}
} else {// 如果没有下一个操作符,则栈内进行加减运算
double after = Convert.ToDouble(numberStack.Pop());
double pre = Convert.ToDouble(numberStack.Pop());
string op = operatorStack.Pop().ToString();
switch (op) {
case "+": { numberStack.Push(pre + after); continue; }
case "-": { numberStack.Push(pre - after); continue; }
default: continue;
}
}
} else {// 如果栈顶操作符为*或者/,则栈内直接进行乘除运算
double after = Convert.ToDouble(numberStack.Pop());
double pre = Convert.ToDouble(numberStack.Pop());
string op = operatorStack.Pop().ToString();
switch (op) {
case "*": { numberStack.Push(pre * after); continue; }
case "/": { numberStack.Push(pre / after); continue; }
default: continue;
}
}
}
// 取出栈顶元素作为结果返回
object result = numberStack.Pop();
return Convert.ToDouble(result);
}
二、通讯录管理的编辑功能
编辑功能一开始没什么太好的方法,就用了一个比较“猥琐”的方式:
- 先选中要编辑的一行->
- 将这一行的数据传递给编辑窗口->
- 删除这一行的数据。
而编辑窗口本质就是一个自带默认数据的增加窗口。
// 点击编辑按钮
private void EditButton_Click(object sender, EventArgs e) {
// 如果有被选中的项
if (listView1.SelectedItems.Count != 0) {
// 将选中行的信息封装到person中
Person editPerson = new Person {
Id = listView1.SelectedItems[0].SubItems[0].Text,
Name = listView1.SelectedItems[0].SubItems[1].Text,
Sex = listView1.SelectedItems[0].SubItems[2].Text,
WorkPlace = listView1.SelectedItems[0].SubItems[3].Text,
Phone = listView1.SelectedItems[0].SubItems[4].Text,
Email = listView1.SelectedItems[0].SubItems[5].Text
};
// 打开编辑窗口,将person传入
editForm = new Edit(editPerson);
editForm.Show();
// 删除选中行的信息
DeleteSelectCol();
// 重新加载数据
Display();
}
}
而接下来就出现问题了,如下图,编辑窗口的任务并没有结束,但表格已经把选中的信息给删了。
后来我去google,简单的了解到这是由于我启动编辑窗口时以show()的方式启动,而show是非模态显示,另一个和它功能相近的方法是showDialog()模态显示,二者区别是:
模态显示后:
- 弹出窗口阻止调用窗口的所有消息响应。
- 只有在弹出窗口结束后调用窗口才能继续。
- 在模态窗口“关闭”后,可以读取模态窗口中信息,包括窗口的返回状态,窗口子控件的值。
非模态显示后:
- 可以在弹出窗口和调用窗口之间随意切换。
- 调用窗口调用show方法后,下面的代码可以立即执行。
- 在非模态窗口关闭后,窗口的所有资源被释放,窗口不存在,无法获取窗口的任何信息。
所以当我用show()显示编辑时,下面的DeleteSelectCol()方法会立即执行。
所以,最后我改用showDialog()来显示编辑窗口,当点击保存时,窗口状态返回OK,并且将DeleteSelectCol()的条件改为窗口返回状态。
最后改写的代码如下:
总结
- 学好数据结构很重要,有了核心的算法,代码只是实现的工具。
- 由于之前写项目时经常遇到增删改查,所以写通讯录管理时感觉非常轻松,思想有了,不管用什么语言上手都很快。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。