软工第三次作业-结对作业
姓名:苏麦耶姆·阿卜杜克热木
学号:3223004731
github 地址: https://gitee.com/suzette11/the-third-assignment/tree/master/
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 45 | 30 |
- Estimate | - 估计这个任务需要多少时间 | 60 | 70 |
Development | 开发 | 180 | 180 |
- Analysis | - 需求分析(包括学习新技术) | 40 | 30 |
- Design Spec | - 生成设计文档 | 45 | 45 |
- Design Review | - 设计复审(和同事审核设计文档) | 30 | 20 |
- Coding Standard | - 代码规范(为目前的开发制定合适的规范) | 25 | 30 |
- Design | - 具体设计 | 60 | 90 |
- Coding | - 具体编码 | 180 | 300 |
- Code Review | - 代码复审 | 40 | 40 |
- Test | - 测试(自我测试,修改代码,提交修改) | 40 | 40 |
Reporting | 报告 | 60 | 90 |
- Test Report | - 测试报告 | 30 | 30 |
- Size Measurement | - 计算工作量 | 40 | 30 |
- Postmortem & Process Improvement Plan | - 事后总结,并提出过程改进计划 | 20 | 30 |
核心方法 | 功能 |
---|---|
init | 初始化并自动调用化简方法 |
simplify | 分数化简(处理假分数、负号、公约数) |
add/__sub__等 | 重载运算符,实现分数四则运算 |
to_string | 转换为规定格式字符串(如2'3/8) |
parse(静态方法) | 从字符串解析为Fraction对象 |
表达式生成模块
核心函数 | 功能 |
---|---|
generate_expressions(count, range_limit) | 生成指定数量的不重复表达式 |
generate_expression(op_count, range_limit) | 递归生成单个表达式(控制运算符数量 1-3 个) |
is_valid_expression(expr) | 验证表达式合法性(如减法非负、除法有效) |
get_expression_fingerprint(expr) | 生成表达式 “指纹” 用于去重(标准化后哈希) |
normalize_expression(expr) | 标准化表达式(移除括号、排序+/×操作数) |
计算模块
核心函数 | 功能 |
---|---|
calculate_expression(expr) | 计算表达式结果(返回Fraction对象) |
infix_to_postfix(infix) | 中缀表达式转后缀表达式(Shunting-yard 算法) |
evaluate_postfix(postfix) | 计算后缀表达式结果(基于栈实现) |
依赖关系:
main.py 依赖所有模块,是流程控制中心;
expression_generator.py 依赖 calculator.py(验证表达式合法性)和 fraction.py(生成随机分数);
calculator.py 依赖 fraction.py(所有计算基于Fraction类);
file_operate.py 依赖 calculator.py(验证答案时重新计算正确结果)。
关键函数流程图
1.分数类
class Fraction:
def init(self, numerator=0, denominator=1, integer=0):
self.integer = integer # 整数部分
self.numerator = numerator # 分子
self.denominator = denominator # 分母
self.simplify() # 初始化时自动化简
def simplify(self):
"""化简分数,处理假分数为带分数"""
# 处理分母为0的情况
if self.denominator == 0:
raise ValueError("分母不能为0")
# 确保分母为正数
if self.denominator < 0:
self.numerator *= -1
self.denominator *= -1
# 合并整数部分和分数部分(假分数处理)
total_num = self.integer * self.denominator + self.numerator
self.integer = 0
self.numerator = total_num
# 提取整数部分
if abs(self.numerator) >= self.denominator:
self.integer = self.numerator // self.denominator
self.numerator = self.numerator % self.denominator
# 化简分数部分
if self.numerator != 0:
gcd_val = self.gcd(abs(self.numerator), self.denominator)
self.numerator //= gcd_val
self.denominator //= gcd_val
# 处理整体为0的情况
if self.integer == 0 and self.numerator == 0:
self.denominator = 1
@staticmethod
def gcd(a, b):
"""最大公约数计算"""
while b:
a, b = b, a % b
return a
def __add__(self, other):
"""加法运算"""
# 转换为假分数
num1 = self.integer * self.denominator + self.numerator
num2 = other.integer * other.denominator + other.numerator
den1, den2 = self.denominator, other.denominator
# 通分计算
new_num = num1 * den2 + num2 * den1
new_den = den1 * den2
return Fraction(new_num, new_den)
# 其他运算符实现(减法、乘法、除法)类似
def __ge__(self, other):
"""大于等于比较,确保减法不出现负数"""
num1 = self.integer * self.denominator + self.numerator
num2 = other.integer * other.denominator + other.numerator
return num1 * other.denominator >= num2 * self.denominator
def to_string(self):
"""转换为规定格式的字符串"""
if self.integer != 0:
if self.numerator == 0:
return f"{self.integer}"
else:
return f"{self.integer}'{self.numerator}/{self.denominator}"
else:
if self.numerator == 0:
return "0"
else:
return f"{self.numerator}/{self.denominator}"
2.表达式生成器
def generate_expression(operator_count, range_limit):
"""递归生成表达式"""
if operator_count == 0:
# 生成数字(自然数或分数)
return generate_number(range_limit)
# 随机选择运算符
op = random.choice(['+', '-', '×', '÷'])
# 分配运算符到左右子表达式
left_ops = random.randint(0, operator_count - 1)
right_ops = operator_count - 1 - left_ops
left_expr = generate_expression(left_ops, range_limit)
right_expr = generate_expression(right_ops, range_limit)
# 处理减法(确保被减数 >= 减数)
if op == '-':
left_val = evaluate_expression(left_expr)
right_val = evaluate_expression(right_expr)
if left_val < right_val:
left_expr, right_expr = right_expr, left_expr
# 处理除法(确保结果为真分数)
elif op == '÷':
left_val = evaluate_expression(left_expr)
right_val = evaluate_expression(right_expr)
# 检查除法是否合法(结果为分数且除数不为0)
if not is_valid_division(left_val, right_val):
return generate_expression(operator_count, range_limit)
# 随机添加括号
if random.random() < 0.3 and left_ops > 0:
left_expr = f'({left_expr})'
if random.random() < 0.3 and right_ops > 0:
right_expr = f'({right_expr})'
# 对+和×进行排序,避免重复题目
if op in ['+', '×']:
# 标准化处理,确保a+b和b+a被视为同一题目
if should_swap(left_expr, right_expr):
left_expr, right_expr = right_expr, left_expr
return f"{left_expr} {op} {right_expr}"
3.题目去重机制
def normalize_expression(expr):
"""将表达式标准化,用于去重判断"""
# 移除括号(简化处理)
expr = expr.replace('(', '').replace(')', '')
# 处理加法和乘法的交换性
tokens = expr.split()
if len(tokens) == 3 and tokens[1] in ['+', '×']:
# 对操作数排序,确保a+b和b+a具有相同的标准化形式
left, op, right = tokens
if compare_expressions(left, right) > 0:
return f"{right} {op} {left}"
return expr
def is_duplicate(expr, existing_exprs):
"""检查表达式是否重复"""
normalized = normalize_expression(expr)
return normalized in existing_exprs
4.答案验证功能
def validate_answers(exercise_file, answer_file):
"""验证答案并生成评分结果"""
correct = []
wrong = []
with open(exercise_file, 'r') as ef, open(answer_file, 'r') as af:
exercises = [line.strip() for line in ef if line.strip()]
answers = [line.strip() for line in af if line.strip()]
for i, (exercise, user_answer) in enumerate(zip(exercises, answers), 1):
# 提取表达式部分
expr = exercise.split('=')[0].strip()
# 计算正确答案
correct_answer = evaluate_expression(expr).to_string()
# 比较答案
if user_answer == correct_answer:
correct.append(i)
else:
wrong.append(i)
# 写入评分结果
with open('Grade.txt', 'w') as f:
f.write(f"Correct: {len(correct)} ({', '.join(map(str, correct))})\n")
f.write(f"Wrong: {len(wrong)} ({', '.join(map(str, wrong))})\n")
性能分析图表
函数名 | 调用次数 | 累计时间 (秒) | 占比 |
---|---|---|---|
normalize_expression | 45218 | 8.23 | 32.1% |
evaluate_expression | 38762 | 6.57 | 25.7% |
generate_expression | 15623 | 4.12 | 16.1% |
Fraction.simplify | 98342 | 3.26 | 12.7% |
is_duplicate | 10000 | 1.89 | 7.4% |
其他函数 | - | 1.52 | 6.0% |
总结
采用 "实现一个功能,测试一个功能" 的策略,例如先完成分数类,通过单元测试验证后再开发表达式生成,降低了集成风险。
通过性能分析工具定位瓶颈(去重和计算占 80% 耗时),针对性优化而非盲目重构,提升了优化效率。