结对作业
李海婷,学号3224004192,GitHub仓库链接:https://github.com/Sleepy-li/3224004192
| 这个作业属于哪个课程 | https://edu.cnblogs.com/campus/gdgy/Class56-Grade2024-CS |
|---|---|
| 这个作业要求在哪里 | https://edu.cnblogs.com/campus/gdgy/Class56-Grade2024-CS/homework/15694 |
| 这个作业的目标 | 实现一个自动生成小学四则运算题目的命令行程序 |
PSP表格:
| PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) |
|---|---|---|
| Planning | 计划 | 300 |
| · Estimate | · 估计这个任务需要多少时间 | 300 |
| Development | 开发 | 305 |
| · Analysis | · 需求分析 (包括学习新技术) | 45 |
| · Design Spec | · 生成设计文档 | 15 |
| · Design Spec | · 设计复审 | 30 |
| · Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 30 |
| · Design | · 具体设计 | 30 |
| · Coding | · 具体编码 | 120 |
| · Code Review | · 代码复审 | 20 |
| · Test | · 测试(自我测试,修改代码,提交修改) | 15 |
| Reporting | 报告 | 70 |
| · Test Repor | · 测试报告 | 20 |
| · Size Measurement | · 计算工作量 | 20 |
| · Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 30 |
| · 合计 | 375 |
一、效能分析
一开始没有设置分母的取值,所以刚开始程序生成不了想要的结果,于是在除法判断出增加了除数!=0的校验,代码成功运行。


使用 Python 内置cProfile进行性能分析,设置sort='cumtime',按照累计执行时间排序。运行生成 10000 道题,从输出结果可以看出,get_normalized_key()累计耗时最高,是整个程序的性能瓶颈。该函数负责递归遍历表达式树生成规范化 key 用于查重,每次生成候选表达式都要调用;随着题目数量增加,哈希集合越来越大,key 生成与哈希比对开销持续上升。其次消耗较大的是eval()函数,用于递归计算表达式的值,校验减法、除法约束。
二、设计实现过程
1、代码组织、模块、类、函数结构
整体分为五大模块:分数格式化模块、表达式树节点类、表达式生成模块、查重模块、批改模块、命令行入口。
类:ExprNode
作用:用二叉树表示算术表达式。
- 属性:
val(叶子节点,Fraction 分数对象);op运算符;left左子树;right右子树。 - 成员方法:
is_leaf():判断是否是叶子(数字节点,不是运算符)eval():递归计算当前表达式的值,返回 Fractionto_string(need_bracket):递归生成带括号的表达式字符串,自动处理括号优先级,输出题目文本get_normalized_key():递归生成规范化 key,用于去重;+和*将左右 key 排序,交换操作数视为同一道题
全局函数列表
format_frac(f:Fraction):将 Fraction 对象转为作业要求的字符串(2'3/8、3/5、自然数)parse_frac(s:str):解析带分数字符串,转回 Fraction(批改模块用)gen_number(r_max):随机生成自然数或者真分数,作为叶子节点gen_expr(op_count, r_max):递归生成一棵合法表达式二叉树,核心生成函数
- 递归终止条件:op_count=0,返回叶子数字节点
- op_count>0:拆分运算符数量,递归生成左、右子树
- 循环随机选运算符,校验约束:减法左≥右;除法除数≠0、结果 0<res<1
generate_exercises(count, r_max):循环生成指定数量题目,调用 gen_expr,利用集合保存规范化 key,去重,收集题目字符串和答案。grade_exercise(ex_file, ans_file, out_file):批改模块,读取 Exercises.txt、Answers.txt,逐题计算标准答案,比对用户答案,输出 Grade.txtmain():命令行参数解析,分发到出题模式 / 批改模式;程序入口。
模块之间关系
main()
├─ 出题模式:调用 generate_exercises()
│ ├─ gen_expr() 生成表达式树 ExprNode
│ │ └─ gen_number() 生成数字叶子
│ └─ ExprNode.get_normalized_key() 查重;ExprNode.to_string()生成题目文本;format_frac格式化答案
└─ 批改模式:调用 grade_exercise()
└─ parse_frac() 解析分数字符串,计算、比对答案
流程图:

三、代码说明
关键代码说明:
关键代码1:ExprNode 类(表达式二叉树,核心数据结构)

解释:二叉树保存表达式结构。get_normalized_key是实现作业要求去重逻辑的核心。
关键代码2:gen_expr,递归生成合法表达式

解释:递归构造表达式树,随机选运算符,循环校验规则。不满足约束就重新随机运算符,直到找到合法运算符。
关键代码3:批改函数 grade_exercise

解释:读取文件,解析自定义带分数格式,转换为 python 表达式计算标准答案;比对用户答案,按要求格式输出。
四、测试运行(直接在终端输入命令)
示例1:正常出题
命令:.\Myapp.exe -n 10 -r 10
输出:Exercises.txt、Answers.txt,共 10 道不重复四则运算题;每题运算符≤3;减法无负数,除法结果为真分数。
截图:

答案:

示例2:参数缺失校验
命令:.\Myapp.exe -n 10
预期:程序打印帮助信息,不生成题目。

示例3:大批量生成
命令:.\Myapp.exe -n 10000 -r 20
预期:成功生成一万道不重复题目与答案文件。


示例4:批改功能
命令:.\Myapp.exe -e Exercises.txt -a Answers.txt
输出 Grade.txt 示例:


五、项目小结
过程中会出现一些问题,但我觉得我自己解决,不能解决的问题我也上网咨询过了,通过本次结对项目的实践,我体会到了不一样的写项目过程,虽然两个人组队会更好的让自己适应以后工作后会出现的合作,但我还是喜欢自己一个人做事。
最后的PSP表格:
| PSP2.1 | Personal Software Process Stages | 实际耗时(分钟) |
|---|---|---|
| Planning | 计划 | 300 |
| · Estimate | · 估计这个任务需要多少时间 | 300 |
| Development | 开发 | 415 |
| · Analysis | · 需求分析 (包括学习新技术) | 45 |
| · Design Spec | · 生成设计文档 | 20 |
| · Design Spec | · 设计复审 | 30 |
| · Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 60 |
| · Design | · 具体设计 | 60 |
| · Coding | · 具体编码 | 120 |
| · Code Review | · 代码复审 | 35 |
| · Test | · 测试(自我测试,修改代码,提交修改) | 45 |
| Reporting | 报告 | 125 |
| · Test Repor | · 测试报告 | 60 |
| · Size Measurement | · 计算工作量 | 20 |
| · Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 45 |
| · 合计 | 540 |
浙公网安备 33010602011771号