首页 > 其他分享 >结对项目

结对项目

时间:2024-09-28 11:12:58浏览次数:8  
标签:结对 项目 expr value 生成 limit expression 表达式

基本信息

这个作业属于哪个课程 软件工程
这个作业要求在哪里 作业要求
成员 李炫杰(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

作业需求

题目:实现一个自动生成小学四则运算题目的命令行程序。

需求:

  1. 使用 -n 参数控制生成题目的个数,例如:Myapp.exe -n 10 将生成10个题目。

  2. 使用 -r 参数控制题目中数值(自然数、真分数和真分数分母)的范围,例如:Myapp.exe -r 10 将生成10以内(不包括10)的四则运算题目。该参数可以设置为1或其他自然数。该参数必须给定,否则程序报错并给出帮助信息。

  3. 生成的题目中计算过程不能产生负数,也就是说算术表达式中如果存在形如e1− e2的子表达式,那么e1≥ e2。

  4. 生成的题目中如果存在形如e1÷ e2的子表达式,那么其结果应是真分数。

  5. 每道题目中出现的运算符个数不超过3个。

  6. 程序一次运行生成的题目不能重复,即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目。生成的题目存入执行程序的当前目录下的Exercises.txt文件。

  7. 在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件。

  8. 程序应能支持一万道题目的生成。

  9. 程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计,输入参数如下: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

实现逻辑:

  • 处理方式:
  1. 生成两个随机操作数。
  2. 随机选择一个运算符。
  3. 如果是除法,则确保分母不为 0,并且结果可以整除。
  4. 通过交换律避免生成相同的加法或乘法表达式。
  • 特殊处理:对于加法和乘法,通过交换律确保不会生成重复表达式。对于除法,调用 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

实现逻辑:

  • 处理方式:
  1. 打开题目文件和答案文件。
  2. 逐行读取题目和答案,对每个题目使用 eval() 计算用户答案和正确答案。
  3. 比较用户答案和正确答案,记录正确或错误的题目编号。
  • 特殊处理:
  1. 使用 try-except 捕获可能出现的文件找不到错误和表达式评估错误,确保程序不崩溃。
  2. 使用 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.txttest_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编程的多个方面,包括文件操作和数学运算。与我的搭档结对工作,我们能够互补技能,共同解决问题,这不仅提高了效率,也增加了学习的乐趣。

郭梓佳:
本次的结对项目,我主要负责文档,报告,博客等方面的编写,辅助设计,编码的实现,记录我们的开发过程。这个经历提高了我的技术写作技能,并加深了我对项目细节的理解。与我的搭档结对工作,我们能够更好地协调信息流,确保了项目的透明度和沟通的流畅性。这种合作方式极大地提升了我的团队协作能力。

标签:结对,项目,expr,value,生成,limit,expression,表达式
From: https://www.cnblogs.com/gdgy/p/18432198

相关文章

  • python处理英语发音音频的项目
    项目需求是这样的:想要获取单词的发音音频,词性,解释,音标,拆分单音标,根据发音拆分音节。实现思路:1、如果是批量处理的话,提前准备好需要处理的单词词库(全是要处理的单词)。2、通过API(你们懂得)可以获取到单词的  词性,解释,发音音频,保存到数据库中,测试单词:conversation3、处......
  • Expo 搭建 RN 项目
    首先,我先问个问题,为什么要写RN项目?答:react-native 相信大部分前端开发都不会陌生——使用JavaScript和React编写原生移动应用。用js就能分ios和android的一杯羹。(哈哈,开个玩笑:)。玩笑归玩笑,但它能说明在开发移动应用领域,RN有它的一席之地。Expo介绍:Expo 搭建......
  • 【Qt】创建一个新项目 &&解析项目代码
    这里写目录标题1.QtCreator创建项目2.项目代码解析2.1main.cpp2.2widget.h2.3widget.cpp2.4.pro工程文件3.中间文件1.QtCreator创建项目首先我们要创建一个项目点击左上角的文件创建新文件选择Application中的QtWidgetsApplication方式进行创建新建项......
  • 旅游项目管理软件10软件,助力行业数字化转型
    市面上主流的10款旅游项目管理系统推荐:PingCode、Worktile、Trello、Asana、Basecamp、Monday.com、Smartsheet、Wrike、旅管家、Travelog。在旅游行业中,项目管理的复杂性常常让人感到不知所措,特别是面对多方协调、行程安排和预算控制时。这些挑战不仅影响团队效率,还可能导致......
  • AB plc设备数据 转profinet IO项目案例
    目录1 案例说明 12 VFBOX网关工作原理 13 准备工作 24 网关采集ABPLC数据 25 用PROFINETIO协议转发数据 46 案例总结 71 案例说明设置网关采集ABPLC数据把采集的数据转成profinetIO协议转发给其他系统。2 VFBOX网关工作原理VFBOX网关是协议转换网关,是把一种协议......
  • 【项目综合】高并发内存池
    目录一、项目背景1)mini版的tcmalloc2)内存池是什么3)C/C++的malloc和new4)所用技术栈和项目环境二、实现定长内存池1)基本框架2)申请内存块3)释放内存块4)细节优化和性能测试 三、高并发内存池的整体框架四、实现threadcache申请内存1)基本框架2)哈希桶中的映射......
  • 一站式解决方案:10款PM工程项目管理软件深度解析
    市面上主流的10款PM工程项目管理系统推荐:PingCode、Worktile、飞书、金和项目管理、致远协同办公、Asana、Trello、JIRA、Basecamp、Monday.com。在现代企业管理中,项目的复杂性常常让人感到无从应对,错失进度和预算控制的情况屡见不鲜。选择合适的PM工程项目管理系统,不仅可以......
  • [Angular] 从零开始使用 Angular CLI 创建 Angular 项目
    一、安装Node.js......
  • 结对项目--四则运算
    结对项目--四则运算这个作业属于哪个课程https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/这个作业要求在哪里https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/homework/13230这个作业的目标四则运算成员3122004564方伟城psp表格PSP2.1Personal......
  • 计算机毕业设计-基于Java+SSM架构的电影购票系统项目开发实战(附源码+论文)
    大家好!我是程序员一帆,感谢您阅读本文,欢迎一键三连哦。......