20162330 结对编程-四则运算(挑战出题)
- 命令行运行
- IDEA测试
- 测试工具exprchecker.exe测试
结对对象:
学号 :20162329
姓名 :张旭升
伙伴本周博客地址:
- 担任角色:(本周角色互换较少)
- 驾驶员:张旭升
- 副驾驶:刘伟康
需求分析:
- 输入:通过命令行参数形式指定题目要求
- 输出:输出题目到文件,一行一个题目
输出结果只出题不判题
- 实现题目去重,为了减小随机空间,要求:
- 操作数为个位整数,即选择范围只能是:
1
,2
,3
,4
,5
,6
,7
,8
,9
这9个数字 - 操作符选择范围只能是:
+
,-
,*
,/
, 还有括号
- 操作数为个位整数,即选择范围只能是:
说明:
运行格式为ExpressionGenerator
为最终执行的类,java ExpressionGenerator <number-of-expressions> <number-of-operator> <output-file-path>
关于上周的输出判题说明:可以使用户在文件中作答后运行另一个程序判断正误且结果写入另一个文件。
如果老师要测试此代码,详见
- 分析:输入输出程序我们在上周就已经实现,只需要修改成指定格式,即传入
args[0]
、args[1]
和args[2]
即可。想要实现题目去重功能,我的思路是“括号转移”(会在设计思路中说明),而张旭升的思路是“答案去重”,然而我的思路我无法实现,张旭升也觉得比较困难,于是他按照“答案去重”的思路设计了这一功能。
设计思路:
UML类图
与上次的类图一样,本次主要实现的是命令行参数传入和题目去重的功能:
① 关于命令行参数传入,设计思路主要是传入三个对应的数组,其中前两个参数为int类型,所以可使用Integer.parseInt()方法进行转化,之后传给生成题目的方法,第三个参数决定传给生成的文件。② 关于实现题目去重,张旭升的思路主要是判断运算得到的答案是否去重,最终实现的也是他的思路,我的思路有些困难,主要是考虑到“题目去重”的范围问题。我主要是阅读了罗杰老师的博客,之后产生了疑问,看了看评论和我的问题基本一样(博客中也有指出)。对于我们来说,可能 1+2+3 和 3+2+1 两道题目一样,但是这两道题的运算顺序不一样,按照正确的运算顺序,一道题目相当于 3+3 ,另一道题相当于 5+1 ,就是说如果小学生算这两道题可能就不能算是重复的题目,所以针对“怎么样的题目算是重复的题目”我想到了一种括号转移方法,先考虑运算符优先级,在对于优先级高的运算符连接的两个数字加一对括号,在按照运算顺序依次进行,之后分别判断这几个数字是否相同,例如刚刚的 3+3 和 5+1 就不同,因此不属于重复的题目。但是如果是1+(2+3)就和3+2+1重复了,所以我想先加括号,看能不能经过转移括号判断题目是否重复,这个思路过于复杂,所以张旭升按照他的思路完成了项目,张旭升在生成题目的方法中将生成的每一个题目都放在一个列表中,然后在每生成一个题目时就会先对题目进行计算,将计算的答案传入另一个列表,然后利用indexOf方法来判断前面放入该列表的答案是否有和该答案重复的,如果有就去掉改答案对应的题目。
关键代码解释
关于命令行参数传入:
Practice io = new IOPractice(); if (args.length != 3) { System.err.println("请按正确格式运行:java ExpressionGenerator"); System.exit(1); } int many = Integer.parseInt(args[0]); int grade = Integer.parseInt(args[1]); if (Integer.parseInt(args[1]) == 0) grade = 1; io.Ti(many,grade); ((IOPractice)io).inFile(args[2]);
以上的代码来自ExpressionGenerator类,实现了按照规定格式传入命令行参数,先对输入的参数进行报错,如果没问题,再使用Integer.parseInt()方法对前两个int类型的参数进行转化,之后传给生成题目的方法,第三个参数传给前两个参数写入的文件。
关于答案去重思路:
for (String i : list1) ti += i; StringTokenizer tokenizer = new StringTokenizer(ti); while (tokenizer.hasMoreTokens()) { char N = tokenizer.nextToken().charAt(0); list3.add(N); } list1.clear(); Collections.sort(list3); for(char i:list3) Ti += i; list3.clear(); ori.evaluate(ti); String restrult = count.evaluate(ori.getMessage()); if (list2.indexOf(restrult) >= 0) if (list4.indexOf(Ti)>=0) j--; else if(Ti.indexOf("(")>=0) j--; else { list4.add(Ti); list.add(ti); } else { list2.add(restrult); list4.add(Ti); list.add(ti); }
- 以上的代码来自Practice类,主要利用
indexOf
方法来判断答案是否重复.list
中存放的是带输出的式子list1
中存放的是一个一个的数和运算符和括号list2
中存放的是答案list3
中存放的是排序或待排序的式子list4
中存放的是存放排序后不重复的式子 利用简易的for循环将其中的元素取出来然后累加在一起就成为了一个完整的运算式 将式子以空格为分隔符分成char类型的单字符存入list3
将list1
清空以便于下一道题目的生成,然后运用sort方法对list3
进行排序 遍历出list3
中的元素累加后赋值给变量Ti 清空list3
便于下一个题目的判断 将生成的题目作为参数传给后缀表达式的转换方法ori.evaluate()
将该题计算的答案传入list2
对答案进行判断,是否list2
中已经存在该答案,如果存在的话就再判断list4
中是否存在排序后依旧相同的式子,如果存在就重新出题,如果不存在就再排除掉因为不同位置加括号所以重复的题目之后将该题目加入输出列表list
,如果list2
中不存在相同答案就把该题目传入输出列表list
等待输出。 之所以要用两次判断是因为这样做增大了随机空间。
测试方法(使用命令行、IDEA及测试工具exprchecker.exe进行测试):
命令行运行:
IDEA:
测试工具exprchecker.exe:
运行过程截图:
命令行结果文件目录及信息:
IDEA生成文件信息:
测试时间结果:
代码托管地址:
commit提交信息:
遇到的困难及解决方法:
1.在使用IDEA测试,传入级别参数是0的时候,程序一直运行了很久,没有终止。
解决方法:看了老师的参考用例,加入了相关的终止语句,即在传入级别参数为0时,将其当成级别1处理,如图所示,便可以使程序正常终止。
2.在老师的用例中找到了语句System.exit(1),不理解其用法。
解决方法:(搜索API和相关资料)
看了exit都知道是终止程序的意思,但是为什么会传入1这个参数,通过查找API,发现输入非0的状态码表示异常终止。 之后又相继了解到,System.exit(0)是正常退出程序,而System.exit(1)或者说非0表示非正常退出程序,还要注意:不管status为何值程序都会退出。 而用这种方式退出程序与return相比又有所不同,return是回到上一层,而System.exit(status)是回到最上层。 当然,在程序中如果不加入此语句的话则会直接抛出异常:3.在使用测试工具进行测试时,测试工具不能运行。
- 解决方法:(搜索并询问张旭升) 我先将.java程序拷贝到测试工具的安装目录下,使用命令行在该目录下编译成功后,进行测试,然而弹出了一个不存在的目录文件,并且我点击终止或者重试之后程序都会自动停止运行,测试了几次不成功,于是在网上搜索,大部分内容都和VC++调试程序有关,但是他们的错误往往弹出的是:
Debug Assertion Failed! Program: D:\wyuStud\debug\wyuStud.exe File: afx.inl Line:157
解决方法也比较普遍,
① 按F5运行你的程序 ② 在出错时,选择“重试” ③ 按ALT+7调出“调用栈”窗口 ④ 双击从上往下的最近一个自己定义的函数,系统会自动把该函数所在的文件显示出来,此时程序就暂停在光标处。一般来说错误就出在这附近。你可以通过查看变量的值来确认 然而我尝试了这种方法,并没有用,于是就去问队友张旭升,他的方法是将测试程序拷贝到IDEA的bin(class文件目录)下运行,成功解决。对结对的小伙伴做出评价:
本周我的队友效率很高地做完了项目,大体代码都是他完成的,而我在本周的星期一到星期四之间对于此项目投入较少,仅仅是看完了题目设计,在星期五才开始思考怎么实现,之后将想法告诉张旭升,我和他都觉得有些复杂,由于我不能按照自己的思路实现去重功能,所以张旭升重新设计了思路并在上一周的基础上加以完善,星期五就搞定了程序,但是本周五、周六我的杂事略有点多,所以前几天只是大概测试了一下,加上前四天投入较少以及本人效率较低,所以只是在今天对此程序进行查阅和修改,张旭升的大体比较完善,所以我修改的部分也很少,只是解决了传入参数报错的问题以及去掉之前没有用到的代码,我本周的进度很拖,下周状态要及时恢复过来。
为自己的队友打分:仅针对本周,由于张旭升完成了主要代码,并且思路巧妙,加上我修改的部分很少,所以从代码实现的角度考虑,我给他90分,我10分。
PSP时间统计:
-
PSP2.1 Personal Software Process Stages 预估耗时(小时) 实际耗时(小时) Planning 计划 1 1 · Estimate · 估计这个任务需要多少时间 1 1 Development 开发 11.5 13.5 · Analysis · 需求分析 (包括学习新技术) 1 3 · Design Spec · 生成设计文档 0.5 0.5 · Design Review · 设计复审 (和同事审核设计文档) 1 0.5 · Coding Standard · 代码规范 (为目前的开发制定合适的规范) 1 0.5 · Design · 具体设计 2 3 · Coding · 具体编码 2 4 · Code Review · 代码复审 2 1 · Test · 测试(自我测试,修改代码,提交修改) 2 1 Reporting 报告 3.5 5.5 · Test Report · 测试报告 2 3 · Size Measurement · 计算工作量 0.5 1 · Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 1 1.5 合计 16 20
附加:老师运行时需要注意的一点问题
- 需要的参与编译的
.java
文件都放在src根目录下。 老师在使用命令行编译时,为了避免中文乱码问题,还请使用javac -encoding UTF-8 XX.java
进行编译。 在使用测试工具exprchecker.exe测试时,可能等待时间较长,请老师耐心等待。