一、前言
7-1 答题判题程序-1
(1)知识点:
字符串解析:使用正则表达式或字符串分割来解析输入的题目信息和答案。
控制结构:使用循环结构来处理多个输入行。
条件判断:根据标准答案判断答题结果。
数据结构:使用数组或集合来存储题目信息和答案。
(2)题量:
输入题目数量。
输入题目内容(包括题号、题目内容和标准答案)。
输入答题信息(多个答案,每个答案对应一个题号)。
(3)难度:
简单:主要是字符串处理和基本的数据结构使用。
(4)特点:
输入的题目信息和答题信息较为简单,只需要处理字符串格式的正确性。
不涉及复杂的逻辑处理,只需简单判断答案是否正确。
7-2 答题判题程序-2
(1)知识点:
字符串解析:进一步使用正则表达式或字符串分割来解析输入的题目信息、试卷信息和答案。
控制结构:使用循环结构来处理多个输入行。
条件判断:根据标准答案判断答题结果,并计算得分。
数据结构:使用 Map 或其他集合类来存储题目信息、试卷信息和答案。
对象设计:可能需要设计类来表示题目、试卷、答案等实体。
错误处理:对输入数据进行验证,并给出错误提示。
(2)题量:
输入题目信息(包括题号、题目内容和标准答案)。
输入试卷信息(包括试卷号、题目编号和对应的分值)。
输入答题信息(包括试卷号、学号和答案)。
难度:
中等:增加了对试卷和答案的管理,需要处理更复杂的输入输出逻辑。
(3)特点:
引入了试卷的概念,需要根据试卷号来组织题目和答案。
需要计算每个答案的得分,并输出总分。
可能会出现题目分值总和不为100的情况,需要处理这种情况。
7-3 答题判题程序-3
(1)知识点:
字符串解析:使用正则表达式或字符串分割来解析输入的题目信息、试卷信息、学生信息、答案以及删除题目信息。
控制结构:使用循环结构来处理多个输入行。
条件判断:根据标准答案判断答题结果,并计算得分。
数据结构:使用 Map 或其他集合类来存储题目信息、试卷信息、学生信息和答案。
对象设计:需要设计类来表示题目、试卷、学生、答案等实体。
错误处理:对输入数据进行验证,并给出错误提示。
动态数据修改:支持删除题目信息,并调整相关的试卷和答案输出。
文件操作:可能涉及到文件读写(题目中未明确提及,但可以作为扩展功能)。
(2)题量:
输入题目信息(包括题号、题目内容和标准答案)。
输入试卷信息(包括试卷号、题目编号和对应的分值)。
输入学生信息(包括学号和姓名)。
输入答题信息(包括试卷号、学号和答案)。
输入删除题目信息(删除指定题号的题目)。
(3)难度:
较难:增加了学生信息管理和题目删除功能,需要处理更加复杂的逻辑关系,并且需要保证系统的健壮性和容错性。
(4)特点:
增加了学生信息管理,需要根据学号和姓名来组织答案输出。
支持删除题目信息,需要处理题目删除后的逻辑调整。
输出信息更为复杂,需要考虑多种提示信息(如题目不存在、答案为空等)。
总结
7-1 是基础版本,主要处理简单的题目信息和答案。
7-2 在此基础上增加了试卷概念,并引入了得分计算。
7-3 在前两个版本的基础上增加了学生信息管理、题目删除等功能,逻辑更为复杂,需要处理更多的异常情况和提示信息。
二、设计与分析
7-1 类图
类图解释
1.Main 类
(1)属性:无
(2)方法:
main(args: String[]):程序入口点,负责读取输入并调用 ExamPaper 和 AnswerSheet 相关方法处理数据。
2.Question 类
(1)属性:
num: int:问题编号。
content: String:问题内容。
standardAnswer: String:标准答案。
(2)方法:
checkAnswer(answer: String): boolean:检查用户答案是否正确。
3.ExamPaper 类
(1)属性:
questions: List
(2)方法:
ExamPaper(testCases: String[]): void:构造方法,从输入中解析问题并存储到 questions 列表中。
checkAnswer(num: int, answer: String): boolean:检查特定编号问题的答案是否正确。
4.AnswerSheet 类
(1)属性:
examPaper: ExamPaper:考试试卷。
answers: Map<Integer, String>:学生答案。
results: Map<Integer, Boolean>:答案的正确性。
(2)方法:
AnswerSheet(examPaper: ExamPaper): void:构造方法,初始化考试试卷。
saveAnswer(num: int, answer: String): void:保存学生答案并检查其正确性。
printResults(): void:打印所有问题的答案及其正确性。
心得
(1)模块化设计:
每个类都有明确的职责,这有助于代码的可读性和可维护性。例如,Question 类专门处理单个问题的信息,而 ExamPaper 类则管理整个试卷的问题集合。
(2)面向对象:
使用类来封装数据和行为,提高了代码的复用性和扩展性。例如,AnswerSheet 类不仅存储答案,还能立即检查答案的正确性。
(3)输入输出流:
使用 Scanner 来读取输入,简洁易懂。但在处理大量数据时,可以考虑使用更高效的输入输出方式,如文件读写。
(4)扩展性:
当未来需要扩展功能时,可以在现有类的基础上进行扩展而不需大量改动现有的代码。例如,如果需要增加新的问题类型或答案类型,可以直接在 Question 类中增加相应的属性和方法。
通过这样的设计和分析,我们可以更好地理解代码结构,并且在未来进行维护和扩展时更加容易。
7-2 类图
类图解释
1.类定义
(1)Question 类代表了一个问题实体,包含了问题的 ID、内容和正确答案。
(2)TestPaper 类代表了一份试卷,它包含了一些属性,如试卷的 ID、问题及其对应的分数、问题的顺序以及总分数。此外,还有方法用来向试卷中添加问题,并检查试卷是否满分为100分。
(3)StudentPaper 类代表了学生的答题纸,它记录了学生 ID 和学生的答案列表。
2.方法
(1)addQuestion(int questionId, int score) 方法用于向试卷中添加一个问题,并更新试卷的总分数。
(2)isFullScore() 方法用于检查试卷的总分数是否为100分。
(3)addAnswer(String answer) 方法用于向学生的答题纸上添加答案。
心得
(1)代码组织:代码清晰地分离了关注点,通过不同的类来管理不同的职责。这使得代码易于理解和维护。
(2)数据结构的选择:使用 HashMap 和 ArrayList 来存储问题和学生的答案,这是一个合理的选择,因为它们提供了快速的查找性能。
(4)扩展性:如果将来需要增加更多的功能,比如支持不同类型的问题(选择题、填空题等),或者更复杂的评分机制,当前的设计可以很容易地进行扩展。
(5)输入输出:主函数处理了输入输出流程,包括从标准输入读取数据,以及根据条件输出警告信息或评分结果。这种直接处理方式简单明了,但在更复杂的应用场景下,可能需要将输入输出逻辑封装到单独的模块中。
7-3 类图
类图解释
1.类定义
(1)Question 类:
包含题目的 ID、内容和正确答案。
提供验证答案的方法 validateAnswer,用于检查用户答案是否正确。
(2)TestPaper 类:
包含试卷的 ID、问题列表和每个问题对应的分数。
提供向试卷中添加问题的方法 addQuestion。
提供计算试卷总分数的方法 getTotalScore。
提供获取问题分数的方法 getScoreForQuestion。
(3)AnswerSheet 类:
表示学生的答题纸,包含试卷的引用、学生的 ID 和学生给出的答案。
包含一系列数组用于存储学生的答案、每个答案的正确性、得分以及题目是否已被删除。
提供记录答案的方法 recordAnswer,并在记录时考虑题目是否已被删除。
提供标记题目为已删除的方法 markQuestionAsDeleted。
提供显示答案内容的方法 displayAnswerContent 和显示得分的方法 displayScores。
2.重点代码解释
(一)解析输入行
(1)正则表达式 #N:(\d+)\s+#Q:(.+?)\s+#A:(.+) 用于匹配输入行中的题目编号、题目内容和正确答案。
(2)matcher.matches() 检查输入行是否符合正则表达式模式。
(3)如果匹配成功,则从匹配组中提取所需的信息,并创建 Question 对象存储到 questionBank 中。
(4)如果输入行不符合预期格式,则打印错误信息。
(二)试卷类(TestPaper)
(三)答题纸类(AnswerSheet)
(1)recordAnswer 方法用于记录学生的答案,并根据题目是否已被删除来确定是否计算分数。
(2)如果题目被删除,则不计算分数,且 correctness 标志设置为 false。
(3)否则,使用 Question 类的 validateAnswer 方法检查答案是否正确,并根据正确性分配分数。
(四)显示答案与分数
(1)使用 StringBuilder 来构建输出字符串,这比字符串拼接更高效。
(2)遍历 scores 数组,根据题目是否被删除来决定是否显示分数。
(3)计算总分,并在最后输出。
3.心得
(1)模块化设计:
代码通过类来区分不同的职责,使得每个类都有明确的任务,易于理解和维护。这种模块化的思想有助于减少代码间的耦合,提高代码的复用性。
(2)数据结构选择:
使用数组来存储学生的答案、正确性、得分和删除标志,这种方式在处理固定大小的数据集合时非常有效。同时,使用列表来存储问题和分数也便于动态增删问题。
(4)性能考虑:
在处理大量数据时,循环遍历数组和列表可能会成为性能瓶颈。在 AnswerSheet 类中,多次遍历数组和列表来处理数据。
(5)安全性:
在 AnswerSheet 类中,通过检查索引是否超出范围来避免非法访问数组,这是一种保证代码安全性的方法。但在实际应用中,还应考虑输入数据的合法性校验,防止恶意输入导致的安全问题。
(6)输入处理:
通过对输入行进行正则匹配和异常处理,可以有效地处理格式不正确的输入数据,确保程序的健壮性。
三、采坑心得
- 输入格式的严格性
问题:例如输入 #N:1 #Q:2+2= #4,因为格式不符合规范,所以直接输出 wrong format
方法:在处理每条输入时,首先验证格式,确保输入符合规范再做进一步解析,可以避免后续的错误。
2.异常处理
问题:在解析输入行时,如果没有考虑到所有可能的异常情况,可能会导致程序崩溃或产生错误的结果。
方法:更详细地捕获并处理各种可能的异常,如 NumberFormatException、IndexOutOfBoundsException 等
3. 数据一致性
问题:在处理多个输入文件或数据流时,如果数据之间存在依赖关系(如学生答案依赖于试卷),需要确保数据的一致性。
方法:
(1)在解析数据之前,先进行数据完整性检查,确保所有必要的数据都已经正确加载。
(2)在异常处理中加入对数据完整性的检查,确保不会因为缺少数据而导致程序出错。
4.顺序号与题号的区分
问题:题号是在题目信息中定义的,而顺序号是试卷中按顺序排列的编号。若未正确区分,可能导致答题解析错误。
方法:明确每个变量的含义,将题号和顺序号分别处理,避免混淆。
5.题目不存在、题目删除与引用错误的处理优先级
问题:如在答题中既引用了不存在的题目又遇到被删除的题目,应该优先输出“题目不存在”的提示信息。
方法:实现时将“题目不存在”作为优先级最高的提示,按优先级输出相关信息,可以减少复杂逻辑判断。
6.学生信息与答卷信息匹配
问题:当答卷中的学号不在学生列表中时,应当输出学号引用错误提示,但题目得分仍需正常输出。
方法:在匹配学生信息时,添加一层判断,如果学号不在学生列表中就输出错误信息,同时记录答卷信息。
7.试卷总分校验
问题:试卷信息中题目的分值可能会出错导致总分不为100。
方法:在所有试卷信息输入后,逐一检查每张试卷的总分并输出提示信息。
四、改进建议
1.建立清晰的数据模型
问题:当前代码可能直接用字典或列表来存储信息,但在复杂信息互相引用的情况下,容易出错。
改进:将题目信息、试卷信息、学生信息、答题信息分别封装为类(如 Question、Paper、Student、Answer 等),并在类内部定义一些基础验证与方法,如计算总分、检查答案正确性等。
2.优化判分逻辑
问题:在判分过程中,题目缺失、答案缺失、题目删除、引用错误等问题需要优先级判断,这部分代码容易变得复杂。
改进建议:
采用字典记录判分逻辑的优先级,例如定义一个 priority_dict = { "answer_missing": 1, "question_deleted": 2, "question_nonexistent": 3 },然后按优先级逐一处理。
可以使用状态机模式,根据不同的输入切换到不同状态,简化条件判断。
3.代码风格与可读性
问题: 当前代码虽然结构清晰,但在某些地方可能存在冗余或不够直观的情况。
改进:
(1)重构冗余代码:对于重复出现的代码片段,考虑抽取成方法或类,提高代码复用率。
(2)注释与文档:增加必要的注释,解释代码的目的和逻辑;编写详细的文档,帮助其他开发者理解代码结构。
4.增加异常处理和边界检查
问题:在初始实现中,可能所有逻辑都写在一起,导致代码臃肿且不易读。
改进:将每个主要功能(如格式验证、数据解析、判题、评分等)独立封装成函数或类。
例如:
parse_question_info():用于解析题目信息;
parse_test_info():解析试卷信息并进行总分校验;
validate_format():统一处理输入数据的格式验证。
5.模块化与功能封装
问题:初始实现可能对输入数据的正确性假设过多,缺乏完整的异常处理。
改进:
(1)在每个数据解析函数中,增加异常处理和边界条件检查,确保即使遇到不完整或错误的输入,程序也能提供合理的错误信息并继续运行;
(2)针对删除题目、答卷信息题号错误等情况,增加相应的提示。
五、总结
1.学习成果
(1)输入解析与数据处理:
掌握了使用正则表达式解析复杂输入的能力。
学会了如何处理各种数据格式,包括题目、试卷、学生信息等。
了解到输入数据的一致性和完整性校验的重要性。
(2)异常处理与错误管理:
学会了如何通过异常处理机制来增强程序的健壮性。
掌握了如何记录详细的错误日志,以便于调试和问题追踪。
明白了用户反馈机制的重要性,能够在程序出现问题时给予用户明确的提示。
(3)面向对象设计与模块化:
学会了如何通过类和对象来组织代码,提高代码的可读性和可维护性。
了解了模块化设计的思想,能够将不同的功能分解成独立的类或模块。
(4)性能优化与代码重构:
学会了如何识别代码中的性能瓶颈,并采取相应的优化措施。
掌握了代码重构的基本技巧,能够对冗余或不直观的代码进行优化。
(5)测试与调试:
学会了如何编写单元测试和集成测试用例,确保代码的质量。
掌握了调试技巧,能够在复杂的环境中定位问题并解决问题。
2.需要进一步学习及研究的地方
(1)输入验证与数据清洗:
进一步学习如何在输入验证过程中处理各种特殊情况,如非法字符、非标准格式等。
研究如何进行更高效的数据清洗,以提高数据处理的速度和准确性。
(2)异常处理与容错机制:
进一步学习如何设计更加完善的异常处理机制,包括异常分类、错误恢复等。
研究如何在分布式环境中实现容错机制,提高系统的可用性。
(3)高级数据结构与算法:
学习更多高级的数据结构(如树、图等),以应对更复杂的业务场景。
研究算法优化技术,提高程序的执行效率。
(4)软件架构设计:
学习软件架构设计的基本原理,了解如何设计可扩展、可维护的系统架构。
研究微服务架构、分布式系统等现代软件架构模式。
3.改进建议及意见
(1)建议增加更多实际案例分析,让我们更好地理解理论知识的应用场景。
(2)作业和实验设计可以更加贴近实际应用场景,让我们有机会解决真实世界中的问题。
(3)建议提供更多的参考资料和示例代码,帮助我们更好地理解作业要求和实现细节。