结对项目
基本信息
| 这个作业属于哪个课程 | 软件工程 | 
|---|---|
| 这个作业要求在哪里 | 作业要求 | 
| 成员 | 李炫杰(3122004953) 郭梓佳(3122004945) | 
| Github地址 | https://github.com/gzjgit-hub/pairing_project.git | 
| 这个作业的目标 | 实现一个自动生成小学四则运算题目的命令行程序 | 
PSP表格
| PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) | 
|---|---|---|---|
| Planning | 计划 | 30 | 30 | 
| · Estimate | · 估计这个任务需要多少时间 | 30 | 30 | 
| Development | 开发 | 790 | 880 | 
| · Analysis | · 需求分析 (包括学习新技术) | 60 | 70 | 
| · Design Spec | · 生成设计文档 | 60 | 60 | 
| · Design Review | · 设计复审 | 40 | 45 | 
| · Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 30 | 45 | 
| · Design | · 具体设计 | 180 | 200 | 
| · Coding | · 具体编码 | 240 | 260 | 
| · Code Review | · 代码复审 | 70 | 80 | 
| · Test | · 测试(自我测试,修改代码,提交修改) | 110 | 120 | 
| Reporting | 报告 | 130 | 80 | 
| · Test Repor | · 测试报告 | 60 | 30 | 
| · Size Measurement | · 计算工作量 | 40 | 30 | 
| · Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 30 | 20 | 
| · 合计 | 950 | 990 | 
作业需求
题目:实现一个自动生成小学四则运算题目的命令行程序。
需求:
- 
使用 -n 参数控制生成题目的个数,例如:Myapp.exe -n 10 将生成10个题目。
 - 
使用 -r 参数控制题目中数值(自然数、真分数和真分数分母)的范围,例如:Myapp.exe -r 10 将生成10以内(不包括10)的四则运算题目。该参数可以设置为1或其他自然数。该参数必须给定,否则程序报错并给出帮助信息。
 - 
生成的题目中计算过程不能产生负数,也就是说算术表达式中如果存在形如e1− e2的子表达式,那么e1≥ e2。
 - 
生成的题目中如果存在形如e1÷ e2的子表达式,那么其结果应是真分数。
 - 
每道题目中出现的运算符个数不超过3个。
 - 
程序一次运行生成的题目不能重复,即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目。生成的题目存入执行程序的当前目录下的Exercises.txt文件。
 - 
在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件。
 - 
程序应能支持一万道题目的生成。
 - 
程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计,输入参数如下:Myapp.exe -e
.txt -a .txt,统计结果输出到文件Grade.txt。  
设计实现过程
程序架构图


程序执行流程逻辑图

模块设计
- 
随机数和表达式生成模块:生成四则运算的随机操作数和表达式。
 - 
结果计算与转换模块:将计算结果转换为分数或带分数形式。
 - 
题目与答案生成模块:生成题目和相应答案并保存到文件中。
 - 
评估与统计模块:读取并评估用户的答案,并保存统计结果。
 - 
用户交互模块:处理用户输入,提供菜单选项,执行生成或评估的操作。
 
调用了哪些库和类
- 
math:
用于进行数学操作。在此代码中主要用于 math.floor(),将浮点数向下取整,确保操作数的合法性,尤其是在乘法和加法的操作中,防止生成相同的表达式。 - 
random:
用于生成随机数和随机选择。在此代码中,通过 random.randint() 生成随机的操作数,并通过 random.choice() 随机选择四则运算符(如加、减、乘、除)。 - 
fractions.Fraction:
用于处理分数。Fraction 用于创建分数对象,允许精确的分数计算,尤其在除法操作中使用,确保结果是以分数表示而不是浮点数。 
关键函数说明
1. create_random_number(value_limit):
随机生成一个自然数或真分数,用于构造四则运算表达式的操作数。
def create_random_number(value_limit):
    if random.random() < 0.5:  # 50% 的概率生成真分数
        numer = random.randint(0, value_limit - 1)
        denom = random.randint(numer + 1, value_limit)
        return Fraction(numer, denom)
    else:  # 50% 的概率生成自然数
        return random.randint(0, value_limit - 1)
实现逻辑:
- 处理方式:该函数根据 50% 的概率决定生成自然数或真分数,使用 random.random() 和 random.randint() 来生成随机数。
 - 特殊处理:对于生成分数,确保分子小于分母,防止生成假分数或无效分数。
 
参数:
- value_limit: 限定生成数值的上限。
 
返回:
- 返回值:生成的自然数或者分数。
 
2. create_single_operation_expression(value_limit):
生成包含一个运算符的简单表达式(加、减、乘、除)。它确保除法不会产生无意义的结果,并且确保加法和乘法遵循交换律,避免重复的表达式。
# 生成包含一个运算符的简单表达式
def create_single_operation_expression(value_limit):
    operand1 = create_random_number(value_limit)
    operand2 = create_random_number(value_limit)
    operator = random.choice(operation_symbols)
    
    # 如果是除法,确保结果为真分数
    if operator == '÷':
        operand1, operand2 = ensure_proper_division(value_limit, operand1, operand2)
    
    # 确保表达式不会出现0作为第一个操作数
    while operand1 == 0:
        operand1 = create_random_number(value_limit)
    
    # 防止交换律导致生成相同的表达式
    if operator in ('+', '×'):
        operand1, operand2 = min(operand1, operand2), max(operand1, operand2)
        if math.floor(operand2) == 0:
            operand2 = random.randint(1, value_limit)
        if min(operand1, operand2) == 0:
            operand1 = random.randint(1, math.floor(operand2))
    
    # 确保不会生成负数结果
    if operator == '-':
        operand1, operand2 = max(operand1, operand2), min(operand1, operand2)
    # 表达式的两种不同格式
    expr = f"{operand1} {operator} {operand2}"
    expr_with_paren = f"({operand1}) {operator} ({operand2})"
    return expr, expr_with_paren
实现逻辑:
- 处理方式:
 
- 生成两个随机操作数。
 - 随机选择一个运算符。
 - 如果是除法,则确保分母不为 0,并且结果可以整除。
 - 通过交换律避免生成相同的加法或乘法表达式。
 
- 特殊处理:对于加法和乘法,通过交换律确保不会生成重复表达式。对于除法,调用 ensure_proper_division() 确保分母和分子的合法性。
 
参数:
- value_limit: 限定生成操作数的上限。
 
返回:
- 返回值:生成的表达式字符串(两种形式:普通和带括号的)。
 
3. create_arithmetic_expression(value_limit):
随机选择生成包含1至3个运算符的表达式,并且保证生成的表达式唯一(不重复)。
# 生成一个随机的表达式(包含1至3个运算符)
def create_arithmetic_expression(value_limit):
    if value_limit < 1:
        raise ValueError("数值范围必须大于等于1")
    while True:
        number_of_operators = random.randint(1, 3)
        if number_of_operators == 1:
            expr, expr_with_paren = create_single_operation_expression(value_limit)
        elif number_of_operators == 2:
            expr, expr_with_paren = create_two_operation_expression(value_limit)
        else:
            expr, expr_with_paren = create_three_operation_expression(value_limit)
        # 确保生成的表达式是唯一的
        if expr not in expression_history:
            expression_history.add(expr)
            return expr, expr_with_paren
实现逻辑:
- 处理方式:根据随机数生成一个包含 1 至 3 个运算符的表达式。调用不同的函数生成单个、两个或三个运算符的表达式。
 - 特殊处理:使用集合 expression_history 存储已生成的表达式,确保生成的表达式是唯一的。
 
参数:
- value_limit: 限定生成操作数的上限。
 
返回:
- 返回值:生成的唯一表达式和带括号的表达式。
 
4. evaluate_answers(exercise_file, solution_file):
读取保存的题目和答案文件,评估用户提交的答案是否正确。通过比较用户答案与正确答案来记录正确和错误的题目编号。
# 统计题目的正确与错误数量
def evaluate_answers(exercise_file, solution_file):
    correct_answers = []
    incorrect_answers = []
    try:
        with open(exercise_file, "r", encoding='utf-8') as q_file, open(solution_file, "r", encoding='utf-8') as s_file:
            for i, (q_line, s_line) in enumerate(zip(q_file, s_file), start=1):
                q_line = q_line.strip()
                s_line = s_line.strip()
                if q_line.startswith("四则运算题目"):
                    q_parts = q_line.split(":", 1)
                    if len(q_parts) == 2:
                        q_line = q_parts[1].strip()
                if s_line.startswith("答案"):
                    s_parts = s_line.split(":", 1)
                    if len(s_parts) == 2:
                        s_line = s_parts[1].strip()
                try:
                    user_solution = eval(s_line.replace('‘', '+'))
                    user_solution = Fraction(user_solution).limit_denominator()
                    correct_solution = eval(wrap_with_parentheses(q_line).replace('÷', '/').replace('×', '*'))
                    correct_solution = Fraction(correct_solution).limit_denominator()
                    if user_solution == correct_solution:
                        correct_answers.append(i)
                    else:
                        incorrect_answers.append(i)
                except Exception as err:
                    print(f"评估第 {i} 题时发生错误:{err}")
        return correct_answers, incorrect_answers
    except FileNotFoundError:
        print("未找到文件")
        return FileNotFoundError
实现逻辑:
- 处理方式:
 
- 打开题目文件和答案文件。
 - 逐行读取题目和答案,对每个题目使用 eval() 计算用户答案和正确答案。
 - 比较用户答案和正确答案,记录正确或错误的题目编号。
 
- 特殊处理:
 
- 使用 try-except 捕获可能出现的文件找不到错误和表达式评估错误,确保程序不崩溃。
 - 使用 Fraction 保证结果是最简分数。
 
参数:
- exercise_file:题目文件名。
 - solution_file:答案文件名。
 
返回:
- 返回值:两个列表,分别存储正确和错误的题目编号。
 
5. wrap_with_parentheses(expr_str):
为表达式的操作数添加括号,确保按照正确的运算顺序进行计算,尤其是复杂表达式中。
# 为所有数加上括号,确保正确的运算顺序
def wrap_with_parentheses(expr_str):
    tokens = []
    num_str = ''
    for char in expr_str:
        if char in '+-×÷':
            tokens.append(num_str)
            tokens.append(char)
            num_str = ''
        else:
            num_str += char
    tokens.append(num_str)
    
    result_expr = ''
    for i, token in enumerate(tokens):
        if token in '+-×÷':
            result_expr += token
        else:
            result_expr += '(' + token + ')'
    
    return result_expr
实现逻辑:
- 处理方式:通过解析表达式字符串,为所有操作数添加括号,确保计算时的运算顺序正确。
 
参数:
- expr_str:表达式的字符串形式。
 
返回:
- 返回值:带括号的表达式字符串。
 
其余调用的函数及其作用
1. 随机数和表达式生成模块
create_two_operation_expression(value_limit):
生成包含两个运算符的表达式,确保生成的结果不会为负数。
# 生成包含两个运算符的表达式
def create_two_operation_expression(value_limit):
    operand3 = create_random_number(value_limit)
    operators_set = ['+', '-']
    second_operator = random.choice(operators_set)
    
    first_expr, first_expr_with_paren = create_single_operation_expression(value_limit)
    # 避免负数结果
    if second_operator == '-':
        while eval(first_expr_with_paren.replace('÷', '/').replace('×', '*')) < operand3:
            operand3 = create_random_number(value_limit)
    
    expr = f"{first_expr} {second_operator} {operand3}"
    expr_with_paren = f"{first_expr_with_paren} {second_operator} ({operand3})"
    return expr, expr_with_paren
create_three_operation_expression(value_limit):
生成包含三个运算符的复杂表达式,同样确保不会生成负数结果。
# 生成包含三个运算符的复杂表达式
def create_three_operation_expression(value_limit):
    operators_set_3 = ['+', '×', '÷']
    third_operator = random.choice(operators_set_3)
    
    first_expr, first_expr_with_paren = create_single_operation_expression(value_limit)
    second_expr, second_expr_with_paren = create_single_operation_expression(value_limit)
    
    expr = first_expr + ' ' + third_operator + ' ' + second_expr
    expr_with_paren = first_expr_with_paren + third_operator + second_expr_with_paren
    # 确保没有负数结果
    while eval(expr_with_paren.replace('÷', '/').replace('×', '*')) < 0:
        first_expr, first_expr_with_paren = create_single_operation_expression(value_limit)
        second_expr, second_expr_with_paren = create_single_operation_expression(value_limit)
        expr = first_expr + ' ' + third_operator + ' ' + second_expr
        expr_with_paren = first_expr_with_paren + third_operator + second_expr_with_paren
    return expr, expr_with_paren
2. 结果计算与转换模块
convert_decimal_to_fraction(decimal_val):
将计算出来的浮点数结果转换为最简分数形式。
# 将计算结果转换为最简分数
def convert_decimal_to_fraction(decimal_val):
    return Fraction(decimal_val).limit_denominator()
convert_improper_fraction(fraction_str):
将假分数转换为带分数,便于结果的展示。
# 将假分数转换为带分数
def convert_improper_fraction(fraction_str):
    numer, denom = map(int, fraction_str.split('/'))
    if numer >= denom:
        integer_part = numer // denom
        remainder = numer % denom
        if remainder == 0:
            return str(integer_part)
        else:
            return f"{integer_part}‘{remainder}/{denom}"
    else:
        return fraction_str
3. 题目与答案生成模块
generate_questions_and_solutions(question_count, value_limit):
根据指定的题目数量和数值范围,生成相应的四则运算题目,并计算其答案。答案被转换为分数,并存储在列表中。
# 生成题目和相应的答案
def generate_questions_and_solutions(question_count, value_limit):
    questions = []
    solutions = []
    for _ in range(question_count):
        expr, expr_with_paren = create_arithmetic_expression(value_limit)
        decimal_result = eval(expr_with_paren.replace('÷', '/').replace('×', '*'))
        fraction_result = convert_decimal_to_fraction(decimal_result)
        if isinstance(fraction_result, int) == False and fraction_result % 1 != 0:
            fraction_result = convert_improper_fraction(f"{Fraction(fraction_result).limit_denominator()}")
        
        questions.append(expr)
        solutions.append(fraction_result)
    return questions, solutions
save_to_file(questions, solutions):
将生成的题目和对应的答案保存到文本文件中,分别为 Exercises.txt 和 Answers.txt。
# 将生成的题目和答案保存到文件中,文件名为 Exercises.txt 和 Answers.txt
def save_to_file(questions, solutions):
    with open("Exercises.txt", "w", encoding='utf-8') as exercise_file, open("Answers.txt", "w", encoding='utf-8') as answer_file:
        for i, (q, a) in enumerate(zip(questions, solutions), start=1):
            exercise_file.write(f"四则运算题目{i}:  {q}\n")
            answer_file.write(f"答案{i}:  {a}\n")
4. 评估与统计模块
save_evaluation_results(correct_answers, incorrect_answers):
将评估的正确与错误的题目编号保存到 Grade.txt 文件中。
# 将评分结果保存到文件,文件名为 Grade.txt,按照指定格式
def save_evaluation_results(correct_answers, incorrect_answers):
    with open("Grade.txt", "w") as results_file:
        results_file.write(f"Correct: {len(correct_answers)} ({', '.join(map(str, correct_answers))})\n")
        results_file.write(f"Wrong: {len(incorrect_answers)} ({', '.join(map(str, incorrect_answers))})\n")
5. 用户交互模块
主程序部分 (if name == "main"):
提供一个简单的菜单,用户可以选择生成题目或评估答案。
如果选择生成题目,程序会提示用户输入题目数量和数值范围,然后调用生成题目和答案的相关函数。
如果选择评估答案,程序会读取题目和答案文件,进行评估并输出结果。
if __name__ == "__main__":
    # 改进的功能菜单,增加独特性
    print("欢迎使用智能算术题生成与评估系统")
    print("请选择功能:\n1. 生成算术题\n2. 统计答题正确率\n")
    user_choice = int(input("请输入对应的数字进行选择:"))
    if user_choice == 1:
        question_count = int(input("请输入需要生成的题目数量:"))
        value_range = int(input("请输入数值范围:"))
        questions, solutions = generate_questions_and_solutions(question_count, value_range)
        save_to_file(questions, solutions)
        print(f"已生成 {question_count} 道题目,并保存至 Exercises.txt 和 Answers.txt 文件中")
    elif user_choice == 2:
        exercise_filename = input("请输入题目文件名(例如 Exercises.txt):")
        answer_filename = input("请输入答案文件名(例如 Answers.txt):")
        correct_answers, incorrect_answers = evaluate_answers(exercise_filename, answer_filename)
        save_evaluation_results(correct_answers, incorrect_answers)
        print("答题统计已保存至 Grade.txt 文件中")
结果展示
生成十道题目后的结果 Exercises.txt和Answers.txt

结果评估

修改前三道题目答案后检验的结果

性能分析
采用pycharm中自带的profile分析性能插件进行分析

图中展示函数之间的调用关系、调用次数和执行时间等信息。
模块部分单元测试展示
测试结果

1.测试生成随机数的功能:test_create_random_number
def test_create_random_number(self):
    # 正常范围测试
    for _ in range(100):
        number = create_random_number(10)
        self.assertTrue(0 <= number < 10)
    
    # 边界测试:数值范围为0或1
    with self.assertRaises(ValueError):
        create_random_number(0)  # 处理范围为0的情况
    self.assertTrue(0 <= create_random_number(1) <= 1)  # 范围为1时只能生成0或1
逻辑:
- 这个测试主要用于验证 
create_random_number函数的行为。 - 第一部分生成了 100 个随机数,确保生成的数都在 
[0, value_limit)的范围内,这里value_limit为 10。 - 第二部分进行边界测试,检查当 
value_limit为 0 或 1 时的行为:- 当 
value_limit为 0 时,函数应当抛出ValueError,因为生成的数无法在该范围内。 - 当 
value_limit为 1 时,生成的数应在[0, 1]范围内。 
 - 当 
 
意图:
确保 create_random_number 函数能够正确生成符合范围的随机数,并在边界条件下正确处理错误。
作用:
验证生成随机数的功能,保证数值范围符合预期,并避免可能出现的错误输入
2.测试生成的表达式是否有重复:test_create_arithmetic_expression_uniqueness
def test_create_arithmetic_expression_uniqueness(self):
    value_limit = 10
    num_expressions = 1000
    generated_expressions = set()
    for _ in range(num_expressions):
        expression, _ = create_arithmetic_expression(value_limit)
        self.assertNotIn(expression, generated_expressions, f"发现重复表达式: {expression}")
        generated_expressions.add(expression)
逻辑:
- 生成 1000 个表达式,并将它们存入一个集合 
generated_expressions。 - 对于每个生成的表达式,检查它是否已存在于集合中。如果存在,说明生成了重复表达式。
 - 如果生成了重复的表达式,则测试失败。
 
意图:
确保 create_arithmetic_expression 函数能够生成唯一的表达式,不会出现重复的情况。
作用:
检测表达式生成器的唯一性,确保每个生成的表达式是不同的,避免生成相同或等效的表达式。
3.测试将小数转换为分数:test_convert_decimal_to_fraction
def test_convert_decimal_to_fraction(self):
    decimal_values = [0.25, 0.5, 0.75, 1.2, 2.5]
    expected_fractions = [Fraction(1, 4), Fraction(1, 2), Fraction(3, 4), Fraction(6, 5), Fraction(5, 2)]
    for decimal, expected in zip(decimal_values, expected_fractions):
        result = convert_decimal_to_fraction(decimal)
        self.assertEqual(result, expected)
逻辑:
- 这个测试将一组小数值转换为分数,并与预期结果进行对比。
 - 使用 
convert_decimal_to_fraction函数将小数转换为最简分数。 - 验证转换结果是否与预期的分数一致。
 
意图:
确保 convert_decimal_to_fraction 函数能够正确将小数转换为最简分数。
作用:
测试小数到分数的转换功能,保证数值转换的正确性,特别是处理有理数的场景。
4. 测试将假分数转换为带分数:test_convert_improper_fraction
def test_convert_improper_fraction(self):
    test_cases = {
        '5/3': '1‘2/3',
        '34/11': '3‘1/11',
        '59/8': '7‘3/8',
        '2612/315': '8‘92/315',
        '383/40': '9‘23/40'
    }
    for improper, expected in test_cases.items():
        result = convert_improper_fraction(improper)
        self.assertEqual(result, expected)
逻辑:
- 这个测试将一组假分数转换为带分数。
 - 使用 
convert_improper_fraction函数将假分数转换为带分数格式,并与预期结果进行比较。 
意图:
确保 convert_improper_fraction 函数能够正确地将假分数转换为带分数,并且格式正确。
作用:
验证带分数转换的功能,保证在数值过大或不符合直观表现的情况下,能够准确显示带分数。
5. 测试为表达式中的所有数加括号:test_wrap_with_parentheses
def test_wrap_with_parentheses(self):
    test_cases = {
        "1 - 0 ÷ 4/5 - 3/7": "(1 )-( 0 )÷( 4/5 )-( 3/7)",
        "7 + 2 - 4 ÷ 3/7": "(7 )+( 2 )-( 4 )÷( 3/7)",
        "1/4 ÷ 2 - 6/7": "(1/4 )÷( 2 )-( 6/7)",
        "4/5 × 6 + 1/3": "(4/5 )×( 6 )+( 1/3)",
        "2 × 5/8": "(2 )×( 5/8)"
    }
    for expression, expected in test_cases.items():
        result = wrap_with_parentheses(expression)
        self.assertEqual(result, expected)
逻辑:
- 为给定的数学表达式中所有的数添加括号,确保每个数都被括起来,以保证表达式的运算顺序。
 - 比较每个表达式的输出是否与预期的带括号格式一致。
 
意图:
验证 wrap_with_parentheses 函数能够正确地为表达式中的所有数字加括号,以保证运算顺序。
作用:
确保表达式在添加括号后仍然符合数学运算的顺序要求,避免因优先级问题导致的错误结果。
6. 测试统计题目正确率的函数:test_evaluate_answers
def test_evaluate_answers(self):
    correct_indices, wrong_indices = evaluate_answers("test_Exercises.txt", "test_Answers.txt")
    self.assertEqual(len(correct_indices), 100)
    self.assertEqual(len(wrong_indices), 0)
逻辑:
- 这个测试检查 
evaluate_answers函数能否正确统计答案的正确和错误的数量。 - 读取测试文件 
test_Exercises.txt和test_Answers.txt,统计正确和错误的答案数量。 - 比较统计结果,确保有 100 个正确答案,0 个错误答案。
 
意图:
确保 evaluate_answers 函数能够正确评估题目并统计正确答案和错误答案。
作用:
测试评估系统的正确性,确保答案文件能够正确匹配题目文件。
7. 测试文件路径不存在的情况:test_invalid_file_paths
def test_invalid_file_paths(self):
    self.assertEqual(evaluate_answers('不存在的文件.txt', '不存在的文件.txt'), FileNotFoundError)
逻辑:
- 测试当题目或答案文件不存在时,
evaluate_answers函数能否正确处理并抛出FileNotFoundError。 - 传入不存在的文件路径,检查是否抛出了相应的错误。
 
意图:
确保在文件路径不正确或文件不存在时,程序能给出正确的异常反馈。
作用:
测试文件处理的健壮性,确保在找不到文件的情况下程序不会崩溃。
8. 测试生成表达式是否合法:test_create_arithmetic_expression
def test_create_arithmetic_expression(self):
    for _ in range(100):
        expression, expression_with_paren = create_arithmetic_expression(10)
        try:
            result = eval(expression_with_paren.replace('÷', '/').replace('×', '*'))
            self.assertIsInstance(result, (int, float))
        except ZeroDivisionError:
            self.fail("生成了包含除以零的表达式")
逻辑:
- 生成 100 个表达式,检查表达式的合法性。
 - 使用 
eval评估表达式,确保其结果是整数或浮点数。 - 捕获可能的 
ZeroDivisionError(除以零),如果出现该错误,测试失败。 
意图:
确保 create_arithmetic_expression 生成的表达式合法,并且不会出现除以零的情况。
作用:
验证表达式生成器的合法性,避免生成无法计算或非法的表达式。
10. 边界测试:极大数值范围:test_large_value_limit
def test_large_value_limit(self):
    value_limit = 10**6  # 设置极大范围
    for _ in range(10):
        expression, expression_with_paren = create_arithmetic_expression(value_limit)
        try:
            result = eval(expression_with_paren.replace('÷', '/').replace('×', '*'))
            self.assertIsInstance(result, (int, float))
        except ZeroDivisionError:
            self.fail("生成了包含除以零的表达式")
逻辑:
- 使用非常大的 
value_limit(1,000,000)进行边界测试,确保表达式生成器在处理大数值时仍能正常工作。 - 检查表达式的合法性,避免除以零。
 
意图:
验证系统在极大数值范围下的稳定性,确保生成的表达式在大范围内仍然有效。
作用:
测试极大数值范围下的表达式生成,确保不会因大数值导致错误。
模块部分异常值处理
(1) 确保分数除法合法性 (ensure_proper_division)
在 ensure_proper_division 函数中,代码确保除法操作合法。主要检查的两个条件:
- 分母不能为零。
 - 分子必须能够被分母整除,确保除法的结果是一个真分数或整除结果。
 
异常处理逻辑:
使用 while 循环不断生成新的随机分子和分母,直到分母不为零且可以整除分子。这是一种简单有效的防止非法除法操作的方式。
while divisor == 0 or dividend % divisor != 0:
    dividend = create_random_number(value_limit)
    divisor = create_random_number(value_limit)
(2) 评估用户答案时的异常处理 (evaluate_answers)
在评估用户的答案时,有几种可能的异常需要处理,例如:
- 文件找不到 (FileNotFoundError)。
 - 评估表达式时可能出现的错误(例如无效的表达式,非法运算符等)。
 
代码中通过 try-except 结构捕获了这些可能的异常,确保程序在出现异常时不会崩溃。
# 捕获文件未找到的异常
except FileNotFoundError:
    print("未找到文件")
    return FileNotFoundError
# 捕获表达式评估中的错误
try:
    user_solution = eval(s_line.replace('‘', '+'))
    user_solution = Fraction(user_solution).limit_denominator()
    correct_solution = eval(wrap_with_parentheses(q_line).replace('÷', '/').replace('×', '*'))
    correct_solution = Fraction(correct_solution).limit_denominator()
    if user_solution == correct_solution:
        correct_answers.append(i)
    else:
        incorrect_answers.append(i)
except Exception as err:
    print(f"评估第 {i} 题时发生错误:{err}")
详细说明:
- FileNotFoundError: 如果用户提供的题目文件或答案文件不存在,程序会捕获该异常并输出相应的提示信息,而不是直接抛出错误并终止程序。
 - Exception: 在评估表达式时,可能会出现语法错误或非法操作。为了避免程序崩溃,使用了一个通用的 except Exception 捕获所有潜在的错误,并打印出错误信息,同时继续评估后续题目。
 
(3) 处理浮点数转分数 (convert_decimal_to_fraction)
在计算表达式结果时,可能会生成浮点数。这时,convert_decimal_to_fraction 函数会将结果转换为最简分数。这是为了避免浮点精度问题,并确保计算结果以分数的形式输出。
没有使用异常处理机制,但通过使用 Fraction(decimal_val).limit_denominator() 确保结果是合法且最简的分数。
def convert_decimal_to_fraction(decimal_val):
    return Fraction(decimal_val).limit_denominator()
(4) 文件写入的安全性 (save_to_file 和 save_evaluation_results)
在保存题目和答案、保存评估结果时,代码使用 with 语句来确保文件在写入操作完成后被正确关闭,避免了资源泄露的风险。
with open("Exercises.txt", "w", encoding='utf-8') as exercise_file, open("Answers.txt", "w", encoding='utf-8') as answer_file:
    ···
这种做法是一种常见的防止异常情况下(如程序中途退出或出现意外错误)文件未被正确关闭的处理方式,属于隐式的异常处理机制。
项目小结
李炫杰:
我主要负责具体设计,具体编码和测试等方面,实现了智能算术题生成与评估系统。这个系统满足了作业的需求,能够随机生成四则运算题,并评估答案。同时实现了生成唯一表达式、确保除法结果为分数、以及将题目和答案保存到文件的功能。这个项目让我更加深入探索了Python编程的多个方面,包括文件操作和数学运算。与我的搭档结对工作,我们能够互补技能,共同解决问题,这不仅提高了效率,也增加了学习的乐趣。
郭梓佳:
本次的结对项目,我主要负责文档,报告,博客等方面的编写,辅助设计,编码的实现,记录我们的开发过程。这个经历提高了我的技术写作技能,并加深了我对项目细节的理解。与我的搭档结对工作,我们能够更好地协调信息流,确保了项目的透明度和沟通的流畅性。这种合作方式极大地提升了我的团队协作能力。
                    
                
                
            
        
浙公网安备 33010602011771号