这个作业属于哪个课程 | https://edu.cnblogs.com/campus/gdgy/CSGrade22-12 |
---|---|
这个作业要求在哪里 | https://edu.cnblogs.com/campus/gdgy/CSGrade22-12/homework/13221 |
这个作业的目标 | 结对合作完成项目 |
两位同学的姓名 | 许莹柔,学号:3222004684 肖晓霞,学号:3222004853 |
Github项目地址 | https://github.com/9650X/exercise/tree/main/exercise |
1.PSP表格
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 25 |
Estimate | 估计这个任务需要多少时间 | 20 | 20 |
Development | 开发 | 200 | 220 |
Analysis | 需求分析 (包括学习新技术) | 60 | 30 |
Design Spec | 生成设计文档 | 20 | 30 |
Design Review | 设计复审 | 30 | 40 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 10 | 5 |
Design | 具体设计 | 20 | 15 |
Coding | 具体编码 | 200 | 300 |
Code Review | 代码复审 | 30 | 20 |
Test | 测试(自我测试,修改代码,提交修改) | 30 | 40 |
Reporting | 报告 | 60 | 70 |
Test Report | 测试报告 | 20 | 10 |
Size Measurement | 计算工作量 | 15 | 15 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 20 | 30 |
合计 | 760 | 900 |
2.效能分析
功能一: 生成题目和答案
我们采用cProfile进行性能分析,情况如下图所示,花费时间为2h。在性能上,在代码中耗时最多的函数为generate_and_write_expressions,平均调用耗时为0.162s,改进的思路为:(1)当表达式的结果为负数时,不用完全重新生成所有的操作数和运算符;(2)在生成操作数和运算符之前,可以预计可能的结果范围,以减少生成负数结果的可能性。
性能分析结果展示如下:
功能二:校对答案
我们采用cProfile进行性能分析,情况如下图所示,花费时间为3h,在性能上,在代码中耗时最多的函数为write_grades,平均调用耗时为0.117s,改进的思路为:通过设置open函数的buffering参数减少磁盘I/O操作的次数。
性能分析结果展示如下:
3.设计实现过程
功能一:类和函数的设计
代码包含1个类:
ArithmeticExpressionGenerator 类:这个类将包含所有生成算术表达式和答案的逻辑。
代码包含7个函数:
generate_number 方法:用于生成自然数或真分数。
generate_true_fraction 方法:用于生成真分数。
generate_operator 方法:用于随机选择运算符。
calculate_expression 方法:用于计算表达式的值。
format_number 方法:用于格式化数字。
generate_expression 方法:用于生成算术表达式。
main 方法:作为程序的入口点,用于解析命令行参数并生成表达式。
这个设计将所有的函数封装在一个类中,使得代码更加模块化和易于维护。同时,通过命令行参数来控制生成表达式的数量和操作数的范围。
功能二:类和函数的设计
代码包含7个函数:
init:构造函数,用于初始化类实例,并接受练习文件和答案文件的路径。
parse_arguments:用于解析命令行参数。
parse_fraction:用于解析分数字符串并返回分数对象。
read_files:用于从指定文件中读取练习和答案。
calculate_expression:用于计算表达式的结果。
check_answers:用于检查答案是否正确。
main:主方法,用于执行整个检查流程
函数之间的关系是线性和模块化的。parse_arguments函数独立运行,用于解析命令行参数并获取练习文件和答案文件的路径。read_files函数根据这些路径读取相应的文件内容。parse_fraction函数被用来将字符串形式的分数解析成Fraction对象,以便进行精确的数学运算。calculate_expression函数接受操作数和运算符列表,计算表达式的结果。check_answers函数遍历练习和答案,利用parse_fraction和calculate_expression函数来验证每个答案的正确性,最后输出正确和错误的答案列表。整个流程由main函数控制,它按顺序调用上述函数来完成整个检查过程。
4.代码说明
功能一:
关键代码流程图:
代码如下
# 生成算术表达式
def generate_expression(range_limit, num_operators):
"""Generate an arithmetic expression."""
operands = [generate_number(range_limit) for _ in range(num_operators + 1)]
operators = [generate_operator(allow_subtract=True, allow_divide=True) for _ in range(num_operators)]
# 确保结果非负
while True:
result = calculate_expression(operands, operators)
if result >= 0:
break
operands = [generate_number(range_limit) for _ in range(num_operators + 1)]
operators = [generate_operator(allow_subtract=True, allow_divide=True) for _ in range(num_operators)]
# 创建表达式字符串,并在需要的地方添加括号
expression = f"{format_number(operands[0])} "
for op_symbol, operand in zip(operators, operands[1:]):
if op_symbol in ['-', '/']:
expression += f" {op_symbol} ({format_number(operand)})"
else:
expression += f" {op_symbol} {format_number(operand)}"
return expression, format_number(result)
generate_expression函数通过随机生成操作数和运算符来构建算术表达式,并确保结果非负。如果表达式计算结果为负,它会重新生成操作数和运算符,直到结果满足要求。最后,函数返回格式化后的表达式和结果。
功能2:
关键代码流程图如下:
代码如下
def write_grades(correct, wrong, grade_file):
"""
Write the grades to a file.
"""
with open(grade_file, 'w', encoding='utf-8') as f_grade:
f_grade.write(f"Correct: {len(correct)} ({', '.join(map(str, correct))})\n")
f_grade.write(f"Wrong: {len(wrong)} ({', '.join(map(str, wrong))})\n")
write_grades函数的思路是将正确和错误的答案列表及其数量写入指定的文件中。它首先打开一个文件用于写入,然后分别计算正确答案和错误答案的数量,并将它们与答案本身一起格式化为字符串,最后将这些信息写入文件的相应行中。这个过程确保了成绩的记录既清晰又易于理解。
5.代码质量分析
功能1代码质量分析
功能2代码质量分析
功能1性能分析代码质量分析
功能2性能分析代码质量分析
功能1测试代码质量分析
功能2测试代码质量分析
功能1代码测试覆盖率
功能2代码测试覆盖率
6.测试运行
测试用例1
预期结果:
Correct:3(4,5,7)
Wrong:7(1,2,3,6,8,9,10)
实际结果:
测试用例2
预期结果:
Correct:3(1,2,3)
Wrong:2(2,3)
实际结果:
测试用例3
预期结果:
Correct:3(1,2,5,6)
Wrong:2(3,4)
实际结果:
测试用例4
预期结果:
Correct:5(1,2,5,6,7)
Wrong:2(3,4)
实际结果:
测试用例5
预期结果:
Correct:8(1,2,3,4,6,7,8,10)
Wrong:3(5, 9, 11)
实际结果:
测试用例6
预期结果:
Correct:4(1,2,5,6)
Wrong:2(3,4)
实际结果:
测试用例7
预期结果:
Correct:2(1,3)
Wrong:1(2)
实际结果:
测试用例8
预期结果:
Correct:2(1,3)
Wrong:1(2)
实际结果:
测试用例9
预期结果:
Correct:3(1,3,4)
Wrong:1(3)
实际结果:
测试用例10
预期结果:
Correct:3(1,2,3,4,7)
Wrong:2(5,6)
实际结果:
通过十个测试用例,实际结果与我们预期的结果一致,所以可以说明我们的程序是正确的。
7.项目小结
在这次结对编程项目中,我们共同撰写了一个博客,不仅锻炼了我们的编程技能,还增强了我们的团队协作能力。以下是我们对这次合作的小结:
项目成败得失:
在做项目过程中,我们定期进行沟通,确保双方对项目的目标和进度有清晰的认识。并根据各自的强项进行了合理的分工,这提高工作效率。同时,我们发现在项目初期对时间的估计过于乐观,导致后期需要加班赶工。
分享经验:
我们应该更早地开始项目,并为不可预见的问题留出更多缓冲时间。
结对感受:
结对编程是一种非常有益的体验。它不仅让我们有机会从对方那里学习,还让我们意识到团队合作的力量。在编码时,有另一双眼睛可以发现错误和潜在的改进点,这大大提高了我们的代码质量。
对彼此的评价:
成员1: 成员2的耐心和对细节的关注给我留下了深刻的印象,代码审查非常细致,帮助我改进了我的编码习惯。
成员2: 成员1的创新思维和快速解决问题的能力对我很有启发,技术洞察力帮助我们克服了一些技术难题。
总的来说,这次结对编程经历对我们两人都是一次宝贵的学习机会,我们期待将来有更多的机会继续合作。