结对项目
这个作业属于哪个课程 | 22级计科12班 |
---|---|
这个作业要求在哪里 | 作业要求 |
这个作业的目标 | 实现一个自动生成小学四则运算题目的命令行程序 |
姓名 | 学号 |
---|---|
徐梓聪 | 3122004797 |
李嘉远 | 3122004784 |
Github项目链接
一、PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 30 |
Estimate | 估计这个任务需要多少时间 | 100 | 120 |
Development | 开发 | 300 | 320 |
Analysis | 需求分析 (包括学习新技术) | 60 | 40 |
Design Spec | 生成设计文档 | 10 | 10 |
Design Review | 设计复审 | 30 | 40 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 30 | 50 |
Design | 具体设计 | 60 | 60 |
Coding | 具体编码 | 60 | 50 |
Code Review | 代码复审 | 30 | 30 |
Test | 测试(自我测试,修改代码,提交修改) | 30 | 30 |
Reporting | 报告 | 60 | 80 |
Test Repor | 测试报告 | 30 | 30 |
Size Measurement | 计算工作量 | 30 | 30 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 30 | 30 |
合计 | 890 | 950 |
二、效能分析
功能一:生成指定数量的题目和答案
由图可知,在该模块中,耗时最多的函数为write_to_file函数,改进思路是:一次性将所有数据写入文件,而不是逐行写入,这样可以减少 I/O 操作的开销。
功能二:评估练习题的答案
由图可知,在该模块中,耗时最多的函数为grade_exercises函数,改进思路是:如果有大量的题目和答案,可以考虑使用集合来存储正确和错误的题目编号,以提高查找效率。
三、设计实现过程
项目结构
函数设计
main类:
def parse_args():解析命令行参数
def main():调用各模块函数
Generate类:
def format_fraction(f):格式化分数为字符串
def generate_question(rng):生成数学题目和答案
def is_unique_expression(expr, existing_expressions):检查表达式是否唯一
def generate_exercises(num_questions, rng):生成指定数量的题目和答案
def write_to_file(filename, data):将数据写入文件
Evaluate类:
def parse_mixed_number(mixed_str):解析混合数为分数对象
def evaluate_expression(expression):计算数学表达式,返回结果为分数对象
def grade_exercises(exercise_file, answer_file):评估练习题的答案
流程分析
四、代码说明
项目关键代码
generate_question函数:
def generate_question(rng):
# 生成数学题目和答案
operators = ['+', '-', '*', '÷']
num_operators = random.randint(1, 3) # 随机操作符数量
operands = []
for _ in range(num_operators + 1):
if random.choice([True, False]):
operand = random.randint(1, rng - 1) # 整数操作数
else:
numerator = random.randint(1, rng - 1)
denominator = random.randint(1, rng - 1)
operand = Fraction(numerator, denominator) # 分数操作数
operands.append(Fraction(operand))
# 构建表达式
expression = str(operands[0])
for i in range(num_operators):
operator = random.choice(operators)
if random.choice([True, False]) and i < num_operators - 1: # 随机加括号
expression += f" {operator} ({operands[i + 1]})"
else:
expression += f" {operator} {operands[i + 1]}"
try:
result = Evaluate.evaluate_expression(expression.replace('÷', '/')) # 计算结果
result = Fraction(result) # 确保结果是分数
if result < 0: # 负数不合要求
return None, None
return format_fraction(result), expression # 返回格式化结果和表达式
except ZeroDivisionError:
return None, None # 处理除零错误
except SyntaxError:
return None, None # 处理语法错误
- 主要思路:通过随机生成操作数和操作符,构建出一个数学表达式,并计算出结果。
grade_exercises函数:
def grade_exercises(exercise_file, answer_file):
# 评估练习题的答案
with open(exercise_file, 'r') as ef, open(answer_file, 'r') as af:
exercises = ef.readlines()
answers = af.readlines()
correct = []
wrong = []
for i, (exercise, answer) in enumerate(zip(exercises, answers)):
exercise = exercise.strip().split(' ', 1)[-1]
answer = answer.strip()
exercise = exercise.replace('梅', '/') # 替换特殊符号
try:
eval_answer = evaluate_expression(exercise) # 计算答案
parsed_answer = parse_mixed_number(answer) # 解析答案
if eval_answer == parsed_answer:
correct.append(i + 1) # 正确答案
else:
wrong.append(i + 1) # 错误答案
except ZeroDivisionError:
print(f"ZeroDivisionError on Exercise {i + 1}")
wrong.append(i + 1)
except Exception as e:
print(f"Error on Exercise {i + 1}: {e}")
wrong.append(i + 1)
return correct, wrong # 返回正确和错误的题目编号
- 主要思路:通过读取练习题和答案文件,逐一评估每道题的答案,并记录正确与错误的题目编号。
五、测试运行
- 测试样例1:
命令:python main.py -n 10000 -r 10
描述:测试10000道题目以及答案的生成
- 测试样例2:
命令:python main.py -n 10 -r 100
描述:测试数字范围0-100的题目以及答案的生成
- 测试样例3:
命令:python main.py -n 0 -r 10
描述:测试生成题目时传入参数n错误的情况
- 测试样例4:
命令:python main.py -n 10 -r -1
描述:测试生成题目时传入参数r错误的情况
- 测试样例5:
命令:python main.py -n 0 -r -1
描述:测试生成题目时传入参数n和r同时错误的情况
- 测试样例6:
命令:python main.py -n 10 -r 10
描述:测试生成10道数字范围为0-10的题目
- 测试样例7:
命令:python main.py -e Exercises.txt -a Myanswers.txt
描述:测试答案校验功能
- 测试样例8:
命令:python main.py -e Exercises.txt -a Myanswers.txt
描述:测试答案校验功能
- 测试样例9:
命令:python main.py -e A.txt -a B.txt
描述:测试答案校验功能传入参数错误情况
- 测试样例10:
命令:python main.py -e -a
描述:测试指令输入错误情况
六、项目小结
通过本次结对项目设计,我们收获了很多团队沟通以及协作的经验。从任务分工到接口设计以及代码风格的统一,我们互相监督鼓励,互相分享设计思路,互相定时讨论得出高效的解决方案。从本次开发中我们也认识到了一个项目的实现往往更需要团队成员之间的协作,成员与成员之间的互相帮助能让我们的项目开发更加高效。