软件工程第三次作业
软件工程第三次作业
软件工程第三次作业
姓名:蒋国栋
学号:3123004570
github仓库:dyLR-036030/xy3
这个作业属于哪个课程 | https://edu.cnblogs.com/campus/gdgy/class12Grade23ComputerScience/homework/13469 |
---|---|
这个作业要求在哪里 | 结对项目 - 作业 - 计科23级12班 - 班级博客 - 博客园 |
这个作业的目标 | 实现一个自动生成小学四则运算程序 |
1. PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 60 | 75 |
· Estimate | · 估计这个任务需要多少时间 | 60 | 75 |
Development | 开发 | 480 | 520 |
· Analysis | · 需求分析(包括学习新技术) | 90 | 100 |
· Design Spec | · 生成设计文档 | 60 | 70 |
· Design Review | · 设计复审(和同事审核设计文档) | 30 | 25 |
· Coding Standard | · 代码规范(为目前的开发制定合适的规范) | 20 | 15 |
· Design | · 具体设计 | 60 | 65 |
· Coding | · 具体编码 | 180 | 190 |
· Code Review | · 代码复审 | 20 | 25 |
· Test | · 测试(自我测试,修改代码,提交修改) | 20 | 30 |
Reporting | 报告 | 60 | 55 |
· Test Report | · 测试报告 | 20 | 25 |
· Size Measurement | · 计算工作量 | 20 | 15 |
· Postmortem & Process Improvement Plan | · 事后总结,并提出过程改进计划 | 20 | 15 |
合计 | 600 | 650 |
2.性能分析
从火山图可知,函数generate_unique_expression()耗时最长。
改进思路:
- 优化表达式生成算法:通过限制尝试次数避免无限循环
- 改进去重机制:使用规范化形式进行快速比较
- 缓存计算结果:在表达式求值时避免重复计算
- 优化数字生成:限制分数分母范围,避免过于复杂的分数
3.设计实现过程
3.1类设计
MathExerciseGenerator (主控制器)
↓
ExpressionGenerator (表达式生成器)
↓
ArithmeticExpression (表达式类)
3.2关键类说明
- ArithmeticExpression类:表示算术表达式,支持二叉树结构
- ExpressionGenerator类:负责生成唯一的表达式
- MathExerciseGenerator类:主控制器,协调整个流程
3.3核心函数调用关系
3.4关键函数流程图
3.4.1表达式生成流程图
3.4.2表达式求值流程图
3.4.3答案检查流程图
3.4.4数字生成流程图
4. 代码说明
关键代码1:表达式求值
python
def evaluate(self) -> Fraction:
"""计算表达式的值"""
if self.is_leaf():
return self.value
left_val = self.left.evaluate()
right_val = self.right.evaluate()
if self.operator == '+':
return left_val + right_val
elif self.operator == '-':
result = left_val - right_val
if result < 0: # 确保结果非负
raise ValueError("计算结果不能为负数")
return result
# ... 其他运算符处理
思路: 使用递归方式计算表达式值,在减法时检查结果是否为负数。
关键代码2:表达式规范化
python
def get_canonical_form(self) -> str:
"""获取表达式的规范形式,用于去重"""
if self.is_leaf():
return str(self.value)
left_canonical = self.left.get_canonical_form()
right_canonical = self.right.get_canonical_form()
# 对于加法和乘法,对操作数排序以实现规范化
if self.operator in ['+', '×']:
if left_canonical > right_canonical:
left_canonical, right_canonical = right_canonical, left_canonical
return f"({left_canonical}{self.operator}{right_canonical})"
思路: 通过交换加法和乘法的操作数顺序,实现表达式的规范化,避免重复题目。
5.测试代码
import unittest
from fractions import Fraction
from main import ArithmeticExpression, ExpressionGenerator, MathExerciseGenerator
class TestArithmeticExpression(unittest.TestCase):
"""测试算术表达式类"""
def test_leaf_expression(self):
"""测试叶子节点表达式"""
expr = ArithmeticExpression(value=Fraction(3, 1))
self.assertEqual(expr.evaluate(), Fraction(3))
self.assertEqual(expr.to_string(), "3")
self.assertTrue(expr.is_leaf())
def test_addition_expression(self):
"""测试加法表达式"""
left = ArithmeticExpression(value=Fraction(2))
right = ArithmeticExpression(value=Fraction(3))
expr = ArithmeticExpression(left=left, right=right, operator='+')
self.assertEqual(expr.evaluate(), Fraction(5))
self.assertEqual(expr.to_string(), "2 + 3")
def test_subtraction_expression(self):
"""测试减法表达式"""
left = ArithmeticExpression(value=Fraction(5))
right = ArithmeticExpression(value=Fraction(2))
expr = ArithmeticExpression(left=left, right=right, operator='-')
self.assertEqual(expr.evaluate(), Fraction(3))
self.assertEqual(expr.to_string(), "5 - 2")
def test_multiplication_expression(self):
"""测试乘法表达式"""
left = ArithmeticExpression(value=Fraction(3))
right = ArithmeticExpression(value=Fraction(4))
expr = ArithmeticExpression(left=left, right=right, operator='×')
self.assertEqual(expr.evaluate(), Fraction(12))
self.assertEqual(expr.to_string(), "3 × 4")
def test_division_expression(self):
"""测试除法表达式"""
left = ArithmeticExpression(value=Fraction(1))
right = ArithmeticExpression(value=Fraction(2))
expr = ArithmeticExpression(left=left, right=right, operator='÷')
self.assertEqual(expr.evaluate(), Fraction(1, 2))
self.assertEqual(expr.to_string(), "1 ÷ 2")
def test_complex_expression(self):
"""测试复杂表达式"""
# (2 + 3) × 4
left = ArithmeticExpression(
left=ArithmeticExpression(value=Fraction(2)),
right=ArithmeticExpression(value=Fraction(3)),
operator='+'
)
right = ArithmeticExpression(value=Fraction(4))
expr = ArithmeticExpression(left=left, right=right, operator='×')
self.assertEqual(expr.evaluate(), Fraction(20))
self.assertEqual(expr.to_string(), "(2 + 3) × 4")
def test_format_number_fraction(self):
"""测试分数格式化"""
# 真分数
self.assertEqual(ArithmeticExpression.format_number(Fraction(1, 2)), "1/2")
# 带分数
self.assertEqual(ArithmeticExpression.format_number(Fraction(7, 2)), "3'1/2")
# 整数
self.assertEqual(ArithmeticExpression.format_number(Fraction(5)), "5")
def test_canonical_form_addition(self):
"""测试加法表达式的规范形式"""
left = ArithmeticExpression(value=Fraction(2))
right = ArithmeticExpression(value=Fraction(3))
expr1 = ArithmeticExpression(left=left, right=right, operator='+')
expr2 = ArithmeticExpression(left=right, right=left, operator='+')
self.assertEqual(expr1.get_canonical_form(), expr2.get_canonical_form())
def test_negative_result_prevention(self):
"""测试防止负数结果"""
left = ArithmeticExpression(value=Fraction(2))
right = ArithmeticExpression(value=Fraction(5))
expr = ArithmeticExpression(left=left, right=right, operator='-')
with self.assertRaises(ValueError):
expr.evaluate()
def test_division_by_zero_prevention(self):
"""测试防止除零"""
left = ArithmeticExpression(value=Fraction(5))
right = ArithmeticExpression(value=Fraction(0))
expr = ArithmeticExpression(left=left, right=right, operator='÷')
with self.assertRaises(ValueError):
expr.evaluate()
class TestExpressionGenerator(unittest.TestCase):
"""测试表达式生成器"""
def setUp(self):
self.generator = ExpressionGenerator(range_max=10)
def test_generate_number_range(self):
"""测试数字生成范围"""
for _ in range(100):
number = self.generator.generate_number()
self.assertTrue(0 <= number < 10)
def test_generate_expression_operator_count(self):
"""测试表达式运算符数量"""
for max_ops in [1, 2, 3]:
expr = self.generator.generate_expression(max_ops)
# 这里可以添加运算符数量检查的逻辑
def test_unique_expression_generation(self):
"""测试唯一表达式生成"""
expressions = set()
for _ in range(50):
expr, value = self.generator.generate_unique_expression()
canonical_form = expr.get_canonical_form()
self.assertNotIn(canonical_form, expressions)
expressions.add(canonical_form)
class TestMathExerciseGenerator(unittest.TestCase):
"""测试数学题目生成器"""
def test_exercise_generation_count(self):
"""测试题目生成数量"""
generator = MathExerciseGenerator(range_max=10)
exercises = generator.generate_exercises(5)
self.assertEqual(len(exercises), 5)
def test_expression_evaluation_safe(self):
"""测试安全表达式求值"""
generator = MathExerciseGenerator(range_max=10)
# 测试简单表达式
result = generator.evaluate_expression_safe("2 + 3")
self.assertEqual(result, Fraction(5))
# 测试分数表达式
result = generator.evaluate_expression_safe("1/2 + 1/3")
self.assertEqual(result, Fraction(5, 6))
# 测试带分数表达式
result = generator.evaluate_expression_safe("1'1/2 + 1")
self.assertEqual(result, Fraction(5, 2))
class TestIntegration(unittest.TestCase):
"""集成测试"""
def test_complete_workflow(self):
"""测试完整工作流程"""
import tempfile
import os
# 生成题目
generator = MathExerciseGenerator(range_max=10)
exercises = generator.generate_exercises(3)
# 保存到临时文件
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt') as ex_file:
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt') as ans_file:
exercise_file = ex_file.name
answer_file = ans_file.name
generator.save_to_files(exercises, exercise_file, answer_file)
try:
# 验证文件内容
with open(exercise_file, 'r') as f:
exercise_lines = f.readlines()
with open(answer_file, 'r') as f:
answer_lines = f.readlines()
self.assertEqual(len(exercise_lines), 3)
self.assertEqual(len(answer_lines), 3)
# 测试答案检查功能
correct, wrong = generator.check_answers(exercise_file, answer_file)
# 因为答案是正确的,所以应该全部正确
self.assertEqual(len(correct), 3)
self.assertEqual(len(wrong), 0)
finally:
# 清理临时文件
os.unlink(exercise_file)
os.unlink(answer_file)
def run_performance_test():
"""性能测试函数"""
import time
print("开始性能测试...")
# 测试生成100道题目的性能
start_time = time.time()
generator = MathExerciseGenerator(range_max=10)
exercises = generator.generate_exercises(100)
end_time = time.time()
print(f"生成100道题目耗时: {end_time - start_time:.2f}秒")
print(f"成功生成题目数量: {len(exercises)}")
# 测试答案检查性能
with open("test_exercises.txt", "w") as f:
for i, (ex, ans) in enumerate(exercises):
f.write(f"{i + 1}. {ex}\n")
with open("test_answers.txt", "w") as f:
for i, (ex, ans) in enumerate(exercises):
f.write(f"{i + 1}. {ans}\n")
start_time = time.time()
correct, wrong = generator.check_answers("test_exercises.txt", "test_answers.txt")
end_time = time.time()
print(f"检查100道题目答案耗时: {end_time - start_time:.2f}秒")
print(f"正确题目数: {len(correct)}, 错误题目数: {len(wrong)}")
# 清理测试文件
import os
os.unlink("test_exercises.txt")
os.unlink("test_answers.txt")
if __name__ == "__main__":
# 运行单元测试
print("运行单元测试...")
unittest.main(verbosity=2, exit=False)
print("\n" + "=" * 50 + "\n")
# 运行性能测试
run_performance_test()
print("\n" + "=" * 50 + "\n")
# 手动测试一些边界情况
print("手动测试边界情况...")
# 测试极小范围
print("测试范围=2的情况...")
try:
generator = MathExerciseGenerator(range_max=2)
exercises = generator.generate_exercises(5)
print(f"成功生成 {len(exercises)} 道题目")
except Exception as e:
print(f"生成失败: {e}")
# 测试极大范围
print("测试范围=100的情况...")
try:
generator = MathExerciseGenerator(range_max=100)
exercises = generator.generate_exercises(5)
print(f"成功生成 {len(exercises)} 道题目")
except Exception as e:
print(f"生成失败: {e}")
所有测试用例都能通过,说明代码具有一定的正确性
6.项目小结
本项目实现了一个面向小学教育的四则运算题目自动生成系统,能够生成符合小学数学教学要求的算术题目,支持题目生成、答案计算、答案检查等功能。
7.程序使用说明
# 生成num1个数值范围在num2以内的题目
python main.py -n num1 -r num2
# 检查答案
python main.py -e Exercises.txt -a Answers.txt