一.前言
在前三次大作业中,设计的均为我们学习生活中常见的答题判题程序,适用于常见的一些网上答题系统,例如我们常见的学习通考试系统。总结前三次大作业,主要知识点第一则为类。利用类能够很好的解决前三次大作业的问题。在后两次大作业中则额外考查了动态数组,链表以及hashmap的知识点(因为存储信息只好用以上知识),难度对于第一次大作业可谓直线上升。同时对于信息的获取也考查到了正则表达式的知识点。总结,前三次大作业难度逐次提升,难但不算很难,题量也适中,但需要花费一定时间,知识点主要考察类的运用,同时也考察到正则表达式的运用,在后两次大作业中还考察到hashmap,动态数组,链表以及接口的相关知识。
二.设计与分析
由于大作业主要得分点以及考察点都在最后一题,故只分析最后一题。由于设计思路的问题,使用到大量if或者循环,复杂度过于巨大(尤其是后两次大作业),故未使用SourceMontor进行测试
1.第一次大作业的分析:
在第一次大作业中,主要输入三种信息:题目数量,题目内容,答题信息。题目数量仅为一个数字,可以直接通过int Sum=input.nextInt()中的Sum获取到,而题目内容分为三个部分:题目序号,题目内容以及题目答案,以"#N:"+题号+" "+"#Q:"+题目内容+" "#A:"+标准答案"格式输入,这时候我们可以先直接获取整行信息,利用正则表达式直接查找获取三个信息,也可以使用String.split()的方法进行分割在进行匹配,我这里使用了前一种方式,设置正则表达式直接查找,这种方式没有考虑到输入信息错误的情况,由于第一次大作业并未设置错误信息的测试点,我便直接忽略了这一风险。使用的正则表达式如下:
其中的serach1和search2用于直接获取题目序号和题目内容,search3获取标准答案,然后根据题目数量进行多次循环获取,获取的内容在相应类中创建一个数组用于保存,同时针对题目序号进行排序。而search3同时也可以用于获取第三个信息答题信息的,输入格式为"#A:"+答案内容。获取到的答案信息也保存到相应类中,这样第一步获取信息就算轻松完成了。
以下则为我设计的类图:
之后进行第二步判题程序的进行,利用输入的答题信息中的答案与题目中的标准答案进行对比,以此来判别答题是否正确,这一步我设计为函数放到试卷类Examination这一个类中。判别成功后则使用数组记入判别的信息,由于输出方式与输入的答题信息有关,我将输出函数放到类TextSolve答卷类中,获取完以上信息后利用类TextSolve中函数toQuestionString进行输出,这次大作业就算完成(测试点全部通过)。
2.第二次大作业的分析:
在第二次大作业中则删去了题目数量这一信息,同时增加了试卷信息,即输入信息有三:题目信息,试卷信息,答卷信息。信息方面变化不大,但由于题目数量这一信息的删去,我们并不知道具体有多少题目,因此数组存储题目就不太可行,这是就需要用到新的知识点,而在这次大作业中我选择使用了动态数组,即ArrayList的使用,来进行信息的存储,而设计的类也需要更改,这次大作业的类图如下:
同时我在每个类中都创建了动态数组ArrayList进行信息方面的存储。要存储信息则需要先获取信息。在这次大作业中的第一难点便是信息的获取,同时由于题目增加了三种信息乱序输入的情况,因此区别于第一次大作业的信息获取,我们需要使用而外的正则表达式匹配输入的信息。而输入的信息都含有#+字母的前缀,依据对前缀的检索就可以轻松获取信息,同时也可以解决信息乱序输入的情况。我在这次使用的正则表达式如下:
其中的search6-8即为对前缀的检索,剩下的则为对信息的获取。由于题目中含有end字段来代表输入结束,因此我们可以设计一个while循环来获取信息,检测到end字段则结束,由于本次大作业也依旧没有加入错误信息的输入,在这里不做考虑。检索信息的代码参考结构如下(仅为参考结构,真实代码不一):
到这里完成了第一步获取信息,难度不算大,可以轻松完成,接下来便是本次大作业的难点,对信息的输出。由于多了试卷的总分警示这一行信息,我们现需要判断试卷信息的分数之和是否为100,由于这一段输出信息为最上行,我们可以在获取信息的循环中同时完成分数的计算,完成对上述信息的输出。从本次大作业的样例中不难看出,输出主要依据答卷中输入的题目顺序而非试卷信息,即输出主要依据答卷信息,因此我将主要的用于输出函数toQuestionString()放到类AnswerPaper答卷类中,依据上一次大作业的设计,我同时将判题程序放在了TextSolve试卷类中,因此则需要在主函数main中创建关于AnswerPaper答卷类的类的对象的数组,在AnswerPaper中应用TextSolve试卷类的判别函数进行题目的判别,记录判别的结果。最后创建boolean类型的数组存储判别结果,同时需要创建动态输出mark记录所得分数,最后进行输出。
3.第三次大作业的分析:
在第三次大作业中则额外增加了学生信息和删除题目信息,相较于第二次大作业则需要多增加学生信息类和删除类,同时由于题目输出信息的难度加大,我专门设置了输出类用于输出条件的主要判断。以下为这次的类图:
对于第一步获取信息同前两次大作业一样,由于题目新增了大量错误信息的输入,我对每一段信息进行String.split()进行分割,用于判断信息的输入格式,由于输出"输入错误信息提示"在最上行,我将其放在获取信息的循环中。循环结构同第二次大作业,以下则为使用到的正则表达式:
获取到信息后判别方式同第二次大作业类似,这里不多做赘述。最大难点在于错误信息的输入,因此我将使用动态数组改为hashmap,利用判断hashmap中的内容进行相应输出,此为我当初设计的原码(依据题目设计,过于复杂,待续改进):
三.踩坑心得
1.第一次大作业:由于第一次大作业较为简单,踩坑点仅为对类的运用不熟悉。
2.第二次大作业:难度相较于第一次更大,输入的信息以及判别方式更加艰难,踩坑较多,比如动态数组为空时会报错,一个类不能多次使用(否则导致类的属性改变,只存到一条信息),输出方式主要依据答卷信息而非试卷信息(在第一次尝试中我设计的输出信息主要依据试卷信息),并且一些坑持续了很久,让我花费了大量时间,这也让我认识到提前做好设计的重要性。
3.第三次大作业:由于难度进一步加大,踩的坑也很多,主要为对于错误信息的输出存在需要按序号输出,例如answer is null,需要依据答卷信息按序号输出,而我只是单单把这条信息放在最后输出。设计的问题原码如下:
这段原码导致输出的answer is null信息只会出现在其他输出信息的最后面,导致部分测试点未通过。同时还有hashmap为空时不能引用,或者进行是否为空的判断,而在这次大作业使用大量hashmap,在排查错误时也耗费大量时间。
四.改进建议
1.对于第一次大作业,由于题目较为简单。仅需简化代码,删除多余的无用代码即可
2.对于第二次大作业,难度更大,可以将动态数组的使用更改为hashmap的使用,对于序号进行查找,能够让输出信息更为方便,同时依据答卷序号重复输入的问题,则可以设置个检测程序,在输入时检测是否已经存在该序号的信息,检测成功则可以增加hashmap的存储避免覆盖,同时对同一个序号的试卷类进行标记,避免输出错误的信息。
3.对于第三次大作业,则是思路设计的改进,由于我一开始使用大量hashmap进行存储,对于题目错误信息按序输出方面略有缺陷,个人认为可以适当使用hashmap,一部分改为使用链表进行存储,应为链表对于序号可以更方便的输出,而试卷信息和答卷信息更适合改为链表存储,可以更好的帮助输出信息,减少不必要的条件判断。同时可以将输出类中的一部分输出内容放到对应类中以减少条件判断,减少复杂程度。例如对于题目内容的输出可以使用题目类中的输出函数解决,参数可以增加String类型代表输入的答案,进行答案的判别同时将判别结果返回并输出该答题情况,利用循环实现输出内容中的对于题目内容及答案判别的输出。同时还能获取判别结果计算分数。
5.总结
对于这三次大作业,主要考察对于类的应用,而经过这三次锻炼,也让我对于类的应用更加熟络,同时对于正则表达式的运用,动态数组以及hashmap的运用更为得心应手,同时也让我认识到对于接口的学习需要进一步加强,对于类的运用也需要多加练习。并且需要改掉看到题目直接写代码的陋习,形成面向对象程序设计的核心素养以及设计思路,对于类的设计更需要系统化,对象化。在这几次大作业中也有许多获得优秀成绩的学生,说明这几次大作业并不算很难,对于我也算一种全方面的锻炼。