第三次作业——四则运算试题生成
此作业要求参见:[https://edu.cnblogs.com/campus/nenu/2018fall/homework/2148]
git地址:https://git.coding.net/qiaojingyu/f4.git
结对伙伴:吴奕瑶
功能1:四则运算:支持出题4个数的四则运算题目
功能2:支持括号
功能3:限定题目数量,打印输出,避免重复
功能4:支持分数出题和运算(未实现)
要求一:参考《构建之法》第4章两人合作,结对编程上述功能,要求每人发布随笔1篇 (代码是共同完成的,博客是分别完成的)。 (1) 给出每个功能的重点、难点、编程收获。(2)给出结对编程的体会,以及 (3) 至少5项在编码、争论、复审等活动中花费时间较长,给你较大收获的事件。
(1)每个功能的重点难点:
功能1:
重点:功能一是生成不带括号的四则运算试题,并能正确计算,并且输出答对的题数。我们通过分析,大致把该功能分为四个模块进行编程:
主函数:调用下边三个函数。
生成随机试题:随机生成四个数字以及三个运算符,并且对数字和运算符进行正确地排列组合,使其生成正确的试题。
转换成逆波兰式:将上个模块生成的试题转换成为逆波兰式。
计算后缀表达式:正确地计算上个模块传递过来的逆波兰式,生成正确答案。
难点:正确地将中缀表达式转换成为逆波兰式。我们首先定义了一个数字和运算符优先级顺序的一个函数。根据数字和运算符的优先级进行压栈和出栈操作。并且在数字与运算符之间插入空格。
部分代码如下:
public int priority(char ch) { switch (ch) { case '+': case '-': return 1; case '*': case '/': return 2; default: return 0; } } ... public String strRPN() { this.init(); ope.Push('#'); for (i = 0; i < strlen - 1; i++) { switch (strOld[i]) { case '+': case '-': case '*': case '/': //判断优先级,进行压栈出栈操作 while (this.priority(ope.Peek()) >= this.priority(strOld[i])) { num.Push(ope.Peek()); strNew += " "; strNew += ope.Peek(); ope.Pop(); } ope.Push(strOld[i]); flag = 1; break; case '(': ope.Push(strOld[i]); break; case ')': while (ope.Peek() != '(') { num.Push(ope.Peek()); strNew += " "; strNew += ope.Peek(); ope.Pop(); } ope.Pop(); break; default: num.Push(strOld[i]); if ((flag == 1) && (i != 0)) { strNew += " "; flag = 0; } strNew += strOld[i]; break; } } while (ope.Peek() != '#') { strNew += " " + ope.Peek(); ope.Pop(); }
功能2:
重点:在生成的试题中添加括号。
难点:如何保证括号成对出现,以及在正确的位置出现(例如不会生成(2)这种情况)。考虑到是四则运算,出现括号的情况较少,我们就采用了枚举的方法。
功能3:
重点:在功能2的基础上,添加的判断的功能。可以根据命令行输入的指令,选择输出试题的数量,并且写入文件。
难点:我们最开始写入文件的函数是循环写入,即不会覆盖掉上一次生成的试题,在多次尝试后找到了合适的函数。
部分代码如下:
System.IO.File.WriteAllText(@"File1.txt", "试题来啦!\r\n", Encoding.UTF8); ... if (args.Length != 2) { Console.WriteLine("输入指令有误!"); } else { if (args[0] != "-c") { Console.WriteLine("输入指令有误!"); } else if(!int.TryParse(args[1],out tmp)) { Console.WriteLine("题目数量必须是 正整数。"); } else if (Convert.ToDecimal(args[1]) <= 0 || args[1].Contains(".")) { Console.WriteLine("题目数量必须是 正整数。"); } else { sum = Convert.ToInt32(args[1]); function3(sum); } }
(2)结对编程的体会
较单独编程来说,结对编程更节省编程的时间。很多时候我想不到的地方,吴奕瑶同学都能及时地给出建议。总结了上次编程的经验,这次我们在编程之前,对各个功能进行了分析,并落实到四个模块上。这样编写起来更有计划性和目的性。虽然过程中有过分歧,但是都通过有效地讨论之后达成了一致。
(3)花费时间较长的事件
- 学习如何进行单元测试,以及编写测试用例:首先是对单元测试的环境配置不熟悉,参考了冉华学长的博客之后配置好了环境。之后是对测试用例地编写,我们对每个功能进行了分析,在编写测试用例时因为要考虑到各种情况,所以花费了很多时间。
- 分析功能实现的过程:因为之前没有编写过四则运算的程序,所以在探讨具体要落实到几个模块实现功能上花费了一些时间。
- 逆波兰式的转化出现错误,找错花费了一些时间:在进行单元测试时,发现逆波兰式地生成有错误。我们找到了相应的代码的逻辑错误,反复经过单元测试,修正了错误。
- 讨论如何添加括号:最开始我们想通过编写算法来生成试题,但是总是会生成错误的试题。最后考虑到四则运算加括号的情况不多,最后采用了枚举的方法。
- 实现功能三批量生成试题写入文件的方法:由于最开始调用的写入文件的方法有错误,所以又去网上搜了很多函数,分析它们所能实现的功能,最后通过尝试找到了能正确写入文件的函数。
要求二:给出照片1张,包括结对的2位同学、工作地点、计算机,可选项包括其他能表达结对编程工作经历的物品或场景。
工作地点:星华公寓 b116
使用乔静玉同学的电脑进行编程
照片: