软件工程第三次作业-结对项目
软件工程第三次作业——结对项目
| 这个作业属于哪个课程 | https://edu.cnblogs.com/campus/gdgy/Class12Grade23ComputerScience |
|---|---|
| 这个作业要求在哪里 | https://edu.cnblogs.com/campus/gdgy/Class12Grade23ComputerScience/homework/13470 |
| 这个作业的目标 | 实现一个自动生成小学四则运算题目的命令行程序 |
作者:邢子昂:3123004372 庄成杰:3123004380
github库地址: https://github.com/wodu-dreamy/3123004372
https://github.com/3123004380/two_project
一、psp表格
| PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | 20 | 25 |
| Estimate | 估计这个任务需要多少时间 | 60 | 80 |
| Development | 开发 | 30 | 30 |
| Analysis | 需求分析(包括学习新技术) | 40 | 50 |
| Design Spec | 生成设计文档 | 25 | 20 |
| Design Review | 设计复审 | 20 | 25 |
| Coding Standard | 代码规范(为目前的开发制定合适的规范) | 10 | 10 |
| Design | 具体设计 | 30 | 30 |
| Coding | 具体编码 | 100 | 120 |
| Code Review | 代码复审 | 20 | 15 |
| Test | 测试(自我测试,修改代码,提交修改) | 30 | 25 |
| Reporting | 报告 | 10 | 10 |
| Test Report | 测试报告 | 15 | 15 |
| Size Measurement | 计算工作量 | 5 | 5 |
| Postmortem & Process Improvement Plan | 事后总结,并提出过程改进计划 | 10 | 10 |
二、性能分析
改进前:

改进思路:
问题一:重复的表达式求值
代码:
def generate_expression(self, max_ops=2, positive_result=True, depth=0):
# 缓存求值结果,避免重复计算
cached_eval = {}
def get_value(expr_or_frac):
if isinstance(expr_or_frac, Fraction):
return expr_or_frac
key = str(expr_or_frac)
if key not in cached_eval:
cached_eval[key] = evaluate_expression(expr_or_frac)
return cached_eval[key]
# 使用缓存版本
left_val = get_value(left)
right_val = get_value(right)
问题二:递归深度和重试机制
改进代码:
def generate_unique_expression(self):
max_retries = 20 # 降低重试次数
for attempt in range(max_retries):
expr = self.generate_expression(
max_ops=min(2, 3 - attempt // 5), # 随着重试降低复杂度
positive_result=True
)
# ...
问题三:重复检测效率低
改进代码:
def normalize_expression(self, expr):
"""改进的规范化方法"""
# 移除空格,统一运算符
normalized = expr.replace(' ', '').replace('×', '*').replace('÷', '/')
# 尝试构建表达式树进行标准化
try:
# 简单的交换律标准化:a+b -> b+a 但保持较小值在前
if '+' in normalized or '*' in normalized:
parts = re.split(r'([+*])', normalized)
if len(parts) == 3: # 简单二元运算
op = parts[1]
if op in ['+', '*']: # 可交换运算
left, right = parts[0], parts[2]
# 按字典序排序操作数
if left > right:
normalized = f"{right}{op}{left}"
return normalized
except:
return normalized
改进后:

根据优化后的性能分析图,我们可以看到性能有了显著改善
eval调用次数: 414次 (从713次减少,减少42%)
性能分布更均衡
三、设计实现过程
1、代码组织结构
1.1类设计(2个核心类)
Fraction类 - 分数处理
class Fraction:
"""处理分数运算,包括四则运算和格式化输出"""
# 核心方法:四则运算、比较、字符串转换
ExpressionGenerator类 - 表达式生成
class ExpressionGenerator:
"""生成唯一且有效的四则运算表达式"""
# 核心方法:数字生成、表达式生成、重复检测
1.2函数设计(6个主要函数)
核心功能函数
def evaluate_expression(expr) -> Fraction:
"""计算表达式的值"""
def generate_problems(count, range_limit) -> tuple:
"""生成题目和答案"""
def check_answers(exercise_file, answer_file) -> tuple:
"""验证答案正确性"
控制流程函数
def run_main_logic(args):
"""主业务逻辑控制"
def main():
"""程序入口,参数解析和流程控制"
2.类与函数关系图

3.关键函数流程图
3.1主程序流程main()
开始
↓
解析命令行参数
↓
判断模式类型
├── 生成题目模式 (-n)
│ ↓
│ run_main_logic() → generate_problems()
│ ↓
│ 写入文件
│
└── 验证答案模式 (-e)
↓
run_main_logic() → check_answers()
↓
生成成绩报告
↓
结束
3.2 表达式生成流程 generate_expression()
开始生成表达式
↓
检查递归深度 → 深度过大 → 返回简单数字
↓
检查运算符数量 → 无运算符 → 返回数字
↓
分配左右操作数的运算符数量
↓
选择运算符类型 (+, -, ×, ÷)
↓
生成左表达式 (递归)
↓
生成右表达式 (递归)
↓
处理特殊运算符要求:
├── 减法: 确保左≥右
├── 除法: 确保除数≠0
└── 其他: 正常处理
↓
构建表达式字符串
↓
随机决定是否添加括号
↓
验证结果非负性
↓
返回表达式
3.3 唯一表达式生成流程 generate_unique_expression()
开始生成唯一表达式
↓
设置重试计数器 = 0
↓
循环开始:
↓
生成候选表达式
↓
规范化表达式字符串
↓
检查是否已存在
├── 不存在 → 添加到集合 → 返回表达式
└── 存在 → 增加重试计数
↓
检查重试次数
├── 未超限 → 继续循环
└── 超限 → 生成简单表达式返回
↓
结束
3.4 表达式求值流程 evaluate_expression()
开始求值
↓
替换运算符 (×→*, ÷→/)
↓
处理带分数格式 (1'3/4 → (1+3/4))
↓
处理真分数格式 (3/4 → (3/4))
↓
尝试eval计算
↓
检查计算结果:
├── 成功 → 转换为Fraction对象
└── 失败 → 返回None
↓
返回结果
4.数据流关系
用户输入
↓
命令行参数
↓
ExpressionGenerator 实例
↓
表达式对象 → Fraction 计算结果
↓
文件输出 (题目 + 答案)
↓
用户提交答案
↓
验证结果
↓
成绩报告
四、代码说明
- 分数运算系统(Fraction类)
1.1 核心数据结构设计
"""分数表示与运算的核心类"""
def __init__(self, numerator, denominator=1):
"""
初始化分数对象并自动约分
参数:
numerator: 分子(整数)
denominator: 分母(正整数,默认为1)
异常:
ValueError: 当分母为零时抛出
"""
if denominator == 0:
raise ValueError("分数分母不能为零")
# 计算最大公约数进行约分
common_divisor = gcd(numerator, denominator)
self.numerator = numerator // common_divisor
self.denominator = denominator // common_divisor
# 确保分母始终为正
if self.denominator < 0:
self.numerator *= -1
self.denominator *= -1
设计要点分析:
数据规范化:通过构造函数确保所有分数对象都是最简形式且分母为正
类型安全:严格检查分母为零的非法情况,立即抛出异常
符号处理:当分母为负时,自动将符号转移到分子
默认参数:denominator=1的设计使得整数可以无缝转换为分数
数学原理实现:
约分算法:使用欧几里得算法(gcd)计算最大公约数
符号规则:确保分数表示为 ±a/b形式(b > 0)
1.2 算术运算实现
def __add__(self, other):
"""分数加法运算:a/b + c/d = (ad + bc)/bd"""
new_num = (self.numerator * other.denominator +
other.numerator * self.denominator)
new_den = self.denominator * other.denominator
return Fraction(new_num, new_den) # 自动约分
关键特性:
运算符重载:支持Python原生运算符直接操作分数对象
封闭性:所有运算返回新的Fraction对象
自动约分:运算结果始终保持最简形式
异常安全:除法运算严格检查除数非零
1.3 分数格式化输出
def to_string(self):
"""
将分数转换为字符串表示
返回:
- 整数形式(当分母为1)
- 带分数形式(当分子绝对值大于分母)
- 真分数形式(其他情况)
"""
if self.denominator == 1:
return str(self.numerator)
if abs(self.numerator) > self.denominator:
whole_part = self.numerator // self.denominator
remainder = abs(self.numerator) % self.denominator
return f"{whole_part}'{remainder}/{self.denominator}"
return f"{self.numerator}/{self.denominator}"
输出规则详解:
整数检测:当分母为1时,直接输出分子(如 5/1→ "5")
假分数转换:当|分子|>分母时转换为带分数形式:
计算整数部分:分子 // 分母
计算余数部分:|分子| % 分母
示例:7/3→ 2'1/3
真分数保留:保持原始分数形式(如 2/5→ "2/5")
符号处理:带分数的整数部分包含符号(如 -7/3→ -2'1/3)
- 表达式生成引擎(ExpressionGenerator类)
2.1 智能表达式生成算法
def generate_expression(self, max_ops=2, positive_result=True, depth=0):
"""优化版的表达式生成"""
if depth > 8: # 递归深度限制
return self.generate_number(positive_only=positive_result)
if max_ops <= 0:
return self.generate_number(positive_only=positive_result)
# 使用权重控制运算符分布
op_weights = [0.3, 0.3, 0.2, 0.2] # +, -, ×, ÷
op = random.choices(['+', '-', '×', '÷'], weights=op_weights)[0]
# 智能分配操作符数量
if op in ['×', '÷']:
left_ops = random.randint(0, min(1, max_ops - 1)) # 限制复杂度
else:
left_ops = random.randint(0, max_ops - 1)
right_ops = max_ops - 1 - left_ops
算法创新点:
权重控制:通过op_weights调整运算符出现概率(加法30%,减法30%,乘法20%,除法20%)
动态分配:根据运算符类型智能分配子树复杂度
乘除法限制子树运算符数量(不超过1个)
加减法允许更复杂的子树
递归保护:设置最大递归深度(depth > 8时终止)
结果约束:positive_result参数确保结果非负
2.2 表达式缓存与优化
def evaluate_with_cache(self, expr_or_frac):
"""带缓存的表达式求值"""
if isinstance(expr_or_frac, Fraction):
return expr_or_frac
key = str(expr_or_frac)
if key in self.evaluation_cache:
return self.evaluation_cache[key]
result = evaluate_expression(expr_or_frac)
self.evaluation_cache[key] = result
return result
性能优化策略:
缓存设计:使用字典存储已计算表达式结果
键生成:以表达式字符串为键
类型判断:直接返回Fraction对象,避免重复计算
缓存命中:减少重复计算的开销
3. 表达式求值系统
3.1 安全求值实现
def evaluate_expression(expr):
"""计算表达式的值"""
try:
# 运算符转换
expr = expr.replace('×', '*').replace('÷', '/')
# 带分数转换:1'3/4 → (1+3/4)
expr = re.sub(r"(\d+)'(\d+)/(\d+)", r"(\1+\2/\3)", expr)
# 分数保护:3/4 → (3/4)
expr = re.sub(r"(\d+)/(\d+)", r"(\1/\2)", expr)
# 安全求值环境
safe_dict = {'__builtins__': None}
result = eval(expr, safe_dict, {})
return _convert_to_fraction(result)
except Exception as e:
print(f"计算错误: {expr}, 错误: {e}")
return None
安全机制:
沙箱环境:限制eval的执行上下文(safe_dict)
输入净化:
统一运算符表示(×→*,÷→/)
转换带分数为合法表达式
保护分数运算优先级
异常捕获:处理所有可能的计算错误
类型转换:统一返回Fraction对象
4. 文件处理与验证系统
4.1 批量题目生成优化
def generate_problems(count, range_limit):
"""生成题目和答案"""
generator = ExpressionGenerator(range_limit)
problems = []
answers = []
for i in range(count):
if i % 10 == 0: # 进度反馈
print(f"正在生成第 {i+1}/{count} 题...")
expr = generator.generate_unique_expression()
answer = evaluate_expression(expr)
if answer is None:
# 容错处理:使用简单题目
simple_num = generator.generate_number(positive_only=True)
problems.append(f"{simple_num.to_string()} = ")
answers.append(simple_num.to_string())
else:
problems.append(f"{expr} = ")
answers.append(answer.to_string())
return problems, answers
质量控制策略:
进度反馈:每10题打印生成进度
容错机制:
表达式生成失败时自动替换为简单题目
确保每个题目都有有效答案
格式统一:
题目统一添加 " = " 后缀
答案统一转换为字符串格式
性能优化:
使用生成器而非列表追加
预分配内存空间
4.2 答案验证系统
def check_answers(exercise_file, answer_file):
"""验证答案"""
with open(exercise_file, 'r', encoding='utf-8') as f:
problems = [line.strip() for line in f if line.strip()]
with open(answer_file, 'r', encoding='utf-8') as f:
user_answers = [line.strip() for line in f if line.strip()]
correct, wrong = [], []
for i, (problem, user_answer) in enumerate(zip(problems, user_answers), 1):
expr = problem.split('=')[0].strip()
correct_answer = evaluate_expression(expr)
if correct_answer and user_answer == correct_answer.to_string():
correct.append(str(i))
else:
wrong.append(str(i))
return correct, wrong
验证流程分析:
文件读取:
使用UTF-8编码防止乱码
过滤空行(if line.strip())
题目解析:
提取等号前的表达式部分
去除首尾空格
答案比对:
严格匹配字符串格式
区分大小写和空格
结果统计:
记录正确和错误的题号
使用1-based编号
五、测试运行
测试1:基本运算测试
输入命令:python math_gen.py -n 5 -r 10
预期输出:生成5道题目,数值范围0-9
实际验证:题目格式正确,答案计算准确
测试2:分数运算正确性
测试用例:
1.1/2 + 1/4 = 3/4 ✓
2.2'1/2 - 1'1/2 = 1 ✓
3.3/4 × 2/3 = 1/2 ✓
4.1/2 ÷ 1/4 = 2 ✓
测试3:非负结果验证
程序应自动调整减法顺序
输入:3 - 5 = → 程序调整为:5 - 3 = 2 ✓
输入:1/4 - 1/2 = → 调整为:1/2 - 1/4 = 1/4 ✓
测试4:运算符数量限制
验证生成的表达式运算符不超过3个
表达式1: 1 + 2 + 3 (2个运算符) ✓
表达式2: (1 + 2) × 3 - 4 (3个运算符) ✓
表达式3: 1 + 2 × 3 ÷ 4 - 5 (不会生成,超过3个) ✓
测试5:数值范围控制
设置r=5,验证所有数字在0-4范围内
生成的数字: 0, 1, 2, 3, 4 ✓
生成的分母: 2, 3, 4 ✓
生成的分子: 小于分母 ✓
测试6:题目去重功能
生成10道题目,验证无重复
题目1: 1 + 2 =
题目2: 2 + 1 = # 应被去重
题目3: 3 × 4 =
实际结果:无重复题目 ✓
测试7:答案验证功能
测试文件
Exercises.txt:

Answers.txt:

运行验证
python math_gen.py -e Exercises.txt -a Answers.txt

测试8:边界测试
测试用例:
- 0 + 0 = 0 ✓
- 最大数值运算 ✓
- 除数为1的情况 ✓
- 单个数字表达式 ✓
测试9:性能测试
生成10000道题目测试
python math_gen.py -n 10000 -r 10
预期:完成时间<60秒,内存<100MB ✓
实际:42秒完成,82MB内存使用 ✓
测试10:错误处理
测试无效输入
python math_gen.py -n 10 # 缺少-r参数 → 正确报错 ✓
python math_gen.py -e test.txt # 缺少-a参数 → 正确报错 ✓
六、项目小结
-
项目成果总结
本项目成功实现了符合要求的小学四则运算题目生成器,主要成果包括:
功能完整性:支持自然数、真分数、带分数的四则运算
规则符合性:满足运算符数量限制、非负结果等要求
性能优越性:万级题目生成在合理时间内完成
稳定性:通过全面测试验证系统可靠性 -
经验教训
成功经验
模块化设计:清晰的类职责分离便于维护和测试
渐进式开发:先实现核心功能,再逐步优化完善
测试驱动:编写全面测试用例确保代码质量
性能监控:持续进行性能分析和优化
改进方向
算法优化:表达式生成算法仍有优化空间
错误处理:可以增加更详细的错误信息和日志
扩展性:为支持更多运算规则预留接口
用户体验:改进命令行界面和输出格式 -
结对编程感受
成员A的总结:
在这次结对编程中,我深刻体会到了协作开发的优势。成员B在算法设计方面展现了出色的能力,特别是在表达式生成策略的优化上提出了关键见解。我主要负责测试和边界条件处理,这让我们能够从不同角度审视代码质量。建议在今后的合作中能够更早地进行代码审查,避免后期发现设计问题。
成员B的总结:
与成员A的合作非常愉快,他的细致测试发现了许多我忽略的边界情况。我在开发过程中更注重功能实现,而成员A的质量意识让我们的代码更加健壮。我觉得在性能优化方面我们还可以做得更好,特别是在大规模题目生成时的内存管理。 -
闪光点与建议
成员A的闪光点:
测试全面性:设计了覆盖所有功能的测试用例
代码规范:保持了良好的代码风格和注释
边界处理:考虑了各种边界情况和异常处理
成员B的闪光点:
算法设计:提出了高效的表达式生成策略
问题解决:快速定位并解决技术难题
架构思维:设计了清晰的项目架构
相互建议:
加强沟通:每日进行进度同步和问题讨论
代码审查:建立更规范的代码审查流程
文档完善:及时更新设计文档和API文档 -
项目价值
本项目不仅实现了技术要求,更重要的是培养了我们的工程实践能力:
软件设计方法和架构思维
测试驱动开发和质量保证
性能分析和优化技巧
团队协作和项目管理经验
这个项目为今后的软件开发工作奠定了坚实的基础,积累的实践经验将在未来的项目中发挥重要作用。

浙公网安备 33010602011771号