程序代码:https://git.coding.net/Vector121/f4.git
结对成员:@刘耀泽
编写思路:
1. 随机生成四个整数和三个操作符,并在其中随机插入括号,最后将其拼成试题字符串。
2. 将中缀表达式转换成后缀表达式并在转换的过程中计算出结果。
3. 显示试题并获取用户输入。
4. 对比用户输入的结果和试题结果,完成判断。
重要功能实现代码:
中缀表达式转换后缀表达式并计算。
/// <summary> /// 中缀表达式转后缀表达式并计算 /// </summary> /// <param name="str">传入中缀表达式</param> /// <returns>计算结果</returns> public static int ChangeExpression(string zStr) { char a = ' '; String[] sp = zStr.Split(a);//字符串数组sp Stack st = new Stack(); //栈st Stack st1 = new Stack(); //int j = 0; int res = 0; for (int i = 0; i < sp.Length; i++) { if (sp[i] != "+" && sp[i] != "-" && sp[i] != "*" && sp[i] != "/" && sp[i] != "(" && sp[i] != ")") { st1.Push(sp[i]); } else { if (st.Count == 0 || IsPriority(sp[i], st.Peek().ToString()) == 1 || sp[i] == "(") { st.Push(sp[i]); } else { while (st.Count != 0) { if (st.Peek().ToString() != "(") { res = calc(Convert.ToInt32(st1.Pop().ToString()), Convert.ToInt32(st1.Pop().ToString()), st.Pop().ToString()); st1.Push(res); } else if (sp[i] != ")") { break; } else if (sp[i] == ")") { if (st.Peek().ToString() == "(") { st.Pop(); break; } else { res = calc(Convert.ToInt32(st1.Pop().ToString()), Convert.ToInt32(st1.Pop().ToString()), st.Pop().ToString()); st1.Push(res); } } } if (sp[i] != ")") { st.Push(sp[i]); } } } } while (st.Count != 0) { res = calc(Convert.ToInt32(st1.Pop().ToString()), Convert.ToInt32(st1.Pop().ToString()), st.Pop().ToString()); st1.Push(res); } return res; }
该功能中将中缀表达式转为后缀表达式是重点和难点,在这上面花费的时间最多。其中这里还有一个地方容易出错,就是在计算后缀表达式的时候,弹出两个操作数进行计算时,先弹出的操作数实际上是第二个数字,后弹出来的实际上是第一个数字,一开始的时候我想当然的将先弹出的数字当做第一个操作数,后弹出的数字当做第二个操作数,结果造成了很长时间困扰。这次失误让我对栈的操作有了更深刻的理解。
生成试题。
/// <summary> /// 生成试题 /// </summary> /// <param name="num1">操作数1</param> /// <param name="num2">操作数2</param> /// <param name="op">操作符</param> /// <param name="flag">随机数</param> /// <param name="res">上次生成试题的结果</param> /// <param name="bracket">上次是否生成括号</param> /// <returns>试题字符串</returns> public static string CreateString(string num1, string num2, string op, int flag, ref int res, ref int bracket) { Random rand = new Random(); string str; if (op == "+" || op == "-") { if (flag == 0) { str = "( " + num1 + " " + op + " " + num2 + " )"; res = calc(Convert.ToInt32(num2), Convert.ToInt32(num1), op); } else if (flag == 1) { str = "( " + num2 + " " + op + " " + num1 + " )"; res = calc(Convert.ToInt32(num2), Convert.ToInt32(num1), op); } else { str = num1 + " " + op + " " + num2; res = calc(Convert.ToInt32(num2), Convert.ToInt32(num1), op); bracket = 0; } } else { if (op == "/") { while (Convert.ToInt32(num1) % Convert.ToInt32(num2) != 0) { num2 = rand.Next(1, 100).ToString(); } } str = num1 + " " + op + " " + num2; res = calc(Convert.ToInt32(num2), Convert.ToInt32(num1), op); } return str; } /// <summary> /// 生成试题 /// </summary> /// <param name="num11">操作数1的int值</param> /// <param name="num1">操作数1</param> /// <param name="num2">操作数2</param> /// <param name="op">操作符</param> /// <param name="flag">随机数</param> /// <param name="res">上次生成试题的结果</param> /// <param name="bracket">上次是否生成括号</param> /// <returns>试题字符串</returns> public static string CreateString(int num11, string num1, string num2, string op, int flag, ref int res, int bracket) { Random rand = new Random(); string str; if (op == "+" || op == "-") { if (flag == 0) { str = "( " + num1 + " " + op + " " + num2 + " )"; res = calc(Convert.ToInt32(num2), res, op); } else if (flag == 1) { str = "( " + num2 + " " + op + " " + num1 + " )"; res = calc(Convert.ToInt32(num2), res, op); } else { str = num1 + " " + op + " " + num2; res = calc(Convert.ToInt32(num2), res, op); bracket = 0; } } else { if (op == "/") { if (bracket == 1) { while (res % Convert.ToInt32(num2) != 0) { num2 = rand.Next(1, 100).ToString(); } } else { while (num11 % Convert.ToInt32(num2) != 0) { num2 = rand.Next(1, 100).ToString(); } } } str = num1 + " " + op + " " + num2; res = calc(Convert.ToInt32(num2), res, op); } return str; }
该功能在给试题随机添加括号的时候非常麻烦,最后采用了上面这种方法:先将两个随机数进行组合,然后随机选择是否添加括号,将最终结果当成一个字符串(操作数)再次和第三个随机进行组合,并且也是随机选择是否添加括号...以此类推,并且保证了结果不会出现无意义的数字,最终生成了表达式。本功能的实现给我开阔了一种新的生成随机字符串的思路,并且运用了重载,使我对函数操作的理解加深了。
功能实现实例:
结对编程的体会:
总结成一句话,两个人就是比一个人强。这次编程首先我要感谢我的队友刘耀泽同学,给我提供了一些编程上的帮助。虽然在结对编程的时候经常会有意见的争执和分歧,但是总体来说还是比个人工作要高效很多。
本次结对编程花费时间较长、收货较大的地方主要是:
1、在编写本程序之前,我们对编写程序所用的语言起了一些争执,最后我们选用了C#语言作为本程序的编程语言。
2、生成试题的过程。实现没有括号的试题生成还是比较简单的,但是一旦加入括号同时还要保证最后的结果是有意义就比较难了。我们想了几种思路最后都没有通过,例如先设定几种添加括号方式的模板,然后随机这几种模板,还有一些根本就实现不了,经过一番努力,最终我们选择了上面这种思路。
3、在测试运算结果的时候,我们发现我们口算的结果和程序运算的结果不相同,经过排查发现了是因为在计算后缀表达式的时候弹出两个操作数进行计算,先弹出的操作数实际上是第二个数字,后弹出来的实际上是第一个数字,这样就像是原本题目是“2-1”,但是程序运算的却是“1-2”的结果,一开始的时候我想当然的将先弹出的数字当做第一个操作数,后弹出的数字当做第二个操作数,这个地方真的是耗费了我们很多时间。
4、生成试题的时候保证生成试题的结果是有意义的,这里刚开始完成功能一的时候还是比较容易的,但是加入括号后就比较复杂了,所以在这里也花费了很长时间。
5、将中缀表达转换成后缀表达式的实现过程。以前学数据结构一直都是理论上的学习,只会在纸上用笔转换,从来没有落实到代码上面,通过这次编程算是跌跌撞撞的实现了这一功能。