结对项目

这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/SoftwareEngineeringClassof2023
这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/SoftwareEngineeringClassof2023/homework/13326
GitHub链接 https://github.com/LinFFF5/Arithmetic-Generator

协作成员:
林赛强 3123004184
莫桂友 3123004191

psp表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 120 150
· Estimate 估计这个任务需要多少时间 140 150
Development 开发 350 400
· Analysis 需求分析 (包括学习新技术) 60 60
· Design Spec 生成设计文档 20 30
· Design Review 设计复审 30 30
· Coding Standard 代码规范 (为目前的开发制定合适的规范) 10 10
· Design 具体设计 30 40
· Coding 具体编码 120 140
· Code Review 代码复审 30 50
· Test 测试(自我测试,修改代码,提交修改) 60 60
Reporting 报告 80 90
· Test Repor 测试报告 30 30
· Size Measurement 计算工作量 20 20
· Postmortem & Process Improvement Plan 事后总结, 并提出过程改进计划 30 30
合计 1130 1290

设计实现过程/代码说明

1. 代码组织结构

类名 作用
Fraction 负责分数的表示、运算和化简
主要函数 作用
simplify() 化简分数
operator+ 分数加法
operator- 分数减法
operator* 分数乘法
operator/ 分数除法
infixToPostfix() 将中缀表达式转换为后缀表达式
evaluatePostfix() 计算后缀表达式
generate_Problrm() 负责随机运算表达式

2. 关键函数

1.随机生成模块

功能:生成题目所需的随机操作数和运算符,控制题目难度和类型。

  • generate_integer(int n) 生成[1, n] 范围内的随机整数。
  • generate_fraction(int n) 生成分子和分母均在 [1, n] 范围内的随机分数。
  • generateOperator() 随机返回 +, -, *, / 中的一个运算符。

2. 表达式解析模块

功能:将用户输入或生成的中缀表达式转换为计算机易处理的后缀表达式(逆波兰式)。

  • isOperator(char c) 判断字符是否为四则运算符。
  • getOperatorPrecedence(char op) 定义运算符优先级,* / 高于 + -
  • infixToPostfix(const string& expr)
    • 作用:使用 栈结构 将中缀表达式转换为后缀表达式。
    • 算法步骤
      1. 分割输入表达式为操作数(整数或分数)和运算符。
      2. 遇到操作数直接加入后缀表达式。
      3. 遇到运算符时,弹出栈中优先级≥当前运算符的运算符,再压入当前运算符。
      4. 表达式结束后,将栈中剩余运算符依次弹出。
    • 示例
      • 输入:"3 + 5'1/2 * 2"
      • 输出:["3", "5'1/2", "2", "*", "+"]
点击查看代码
vector<string> infixToPostfix(const string& expression) {
    std::stack<char> operators;
    std::vector<std::string> postfix;
    std::istringstream tokens(expression);
    std::string token;
    while (tokens >> token) {
        if (isdigit(token[0]) || token.find('/') != std::string::npos) {
            // 操作数(整数或分数)
            postfix.push_back(token);
        }
        else if (isOperator(token[0])) {
            // 运算符
            while (!operators.empty() 
                && getOperatorPrecedence(operators.top()) >= getOperatorPrecedence(token[0])) {
                postfix.push_back(std::string(1, operators.top()));
                operators.pop();
            }
            operators.push(token[0]);
        }
        else {
            cout<< "Invalid character in expression.";
        }
    }
// 将剩余的运算符添加到后缀表达式
while (!operators.empty()) {
    postfix.push_back(std::string(1, operators.top()));
    operators.pop();
}

return postfix;
}

3. 表达式计算模块

功能:对后缀表达式进行求值,支持分数运算和带分数处理。

  • evaluatePostfix(const vector<string>& postfix)**

    • 作用:通过栈结构计算后缀表达式的值。

    • 实现步骤

      1. 遍历后缀表达式:
      • 操作数:解析为 Fraction 对象并入栈。
        • 处理带分数(如 3'1/2):转换为假分数 (3*2 + 1)/2 = 7/2
        • 处理普通分数(如 5/3):直接构造 Fraction(5,3)
        • 处理整数(如 4):构造 Fraction(4,1)
      1. 运算符:弹出栈顶两个操作数进行计算,结果压栈。
      2. 最终栈顶元素为计算结果。
  • calculate(const string& expr)** 整合表达式转换和计算流程。

4. 题目生成模块

功能:生成符合数学规则的算术题目,确保减法不产生负数。

  • generate_Problem(string& problem, int r)

    • 核心流程

      1. 确定运算符数量:随机生成 1-3 个运算符,控制题目复杂度。
      2. 生成首操作数:随机选择生成整数或分数。
      3. 迭代生成后续操作数
      • 加法/乘法:任意生成操作数。
      • 减法:生成一个 ≤ 当前结果的操作数(通过 generate_fraction_with_max 实现)。
      • 除法:生成非零分母。

      实时计算中间结果:用于约束后续操作数的生成范围。

点击查看代码
string generate_Problem(string& problem, int r) {

    string result;  // 存储计算结果
    int operator_sum = (rand() % 3) + 1; // 运算符个数(1-3)
    bool isFractionProblem = ifgenerateFraction();  // 判断是否为分数题
    string operand1_str;

    if (isFractionProblem) {
        operand1_str = generate_fraction(r).toString(); // 1 生成初始分数
    }
    else {
        operand1_str = to_string(generate_integer(r)); // 0 生成初始整数
    }

    problem = operand1_str;
    result = operand1_str;

    for (int i = 0; i < operator_sum; ++i) {
        char op = generateOperator(); // 随机生成运算符
        Fraction current_value = parseResult(result); // 解析当前结果为Fraction

        // 生成第二个操作数
        string operand2_str;
        if (op == '-') {
            if (isFractionProblem) {
                // 生成不超过current_value且分母不超过r的分数
                Fraction operand2 = generate_fraction_with_max(r, current_value);
                operand2_str = operand2.toString();
            }
            else {
                // 生成不超过current_value的整数
                int max_op = current_value.integer - 1;
                if (max_op <= 0) {
                    max_op = 1;
                }
                int operand2_int = generate_integer(max_op);
                operand2_str = to_string(operand2_int);
            }
        }
        else {
            if (isFractionProblem) {
                operand2_str = generate_fraction(r).toString();
            }
            else {
                operand2_str = to_string(generate_integer(r));
            }
        }

        problem += " " + std::string(1, op) + " " + operand2_str;

        // 实时计算中间结果 用于约束后续操作数的生成范围
        Fraction operand2 = parseResult(operand2_str);
        Fraction new_result;
        switch (op) {
        case '+': new_result = current_value + operand2; break;
        case '-': new_result = current_value - operand2; break;
        case '*': new_result = current_value * operand2; break;
        case '/':
            if (operand2.integer == 0 && operand2.numerator == 0) {
                cout << "division by zero.";
            }
            new_result = current_value / operand2;
            break;
        }
        result = new_result.toString();
    }
    return result;
}

5. 辅助工具模块

功能:提供数据转换和生成支持。

  • parseResult(const string& str)
    • 作用:将字符串解析为 Fraction 对象,支持三种格式:
      • 整数"5"Fraction(5, 1)
      • 分数"3/4"Fraction(3,4)
      • 带分数"2'1/3"Fraction(2*3 +1, 3) = Fraction(7,3)
  • generate_fraction_with_max(int range, const Fraction& max)
    • 作用:生成一个分母 ≤ range 且值 ≤ max 的分数。
    • 实现:通过循环生成随机分数,直到满足 生成的分数 ≤ max

模块间协作示例

  1. 生成题目
    • generate_Problem 调用 generate_integergenerate_fraction 生成操作数,generateOperator 生成运算符。
    • 每次生成新操作数后,调用 calculate 计算当前表达式结果,用于后续操作数生成。
  2. 计算答案
    • 用户输入表达式后,调用 infixToPostfix 转换为后缀表达式,再通过 evaluatePostfix 计算结果。
  3. 异常处理
    • 若生成的分母为零,构造函数自动修正;若计算时除数为零,抛出异常并由调试者捕获处理。

效能分析

  1. 字符串分割效率低,使用 istringstream 按空格分割表达式,每次 >> 操作会产生临时字符串对象,对长表达式(如含多个分数和运算符)会有显著开销。
  2. Fraction::toString() 分数转字符串函数,频繁使用 stringstream,stringstream 内部动态分配内存,且每次调用会初始化流对象,效率较低。
    带分数格式如 3'1/2 需要多次拼接字符串,导致内存操作频繁。

运行结果:

项目小结

结对项目总结
莫桂友:结对项目让我意识到每个人都有独特的优势和不足。通过与伙伴的合作,我们能够互相补充。同时从伙伴身上学到了许多新的技术和解决问题的方法,这让我受益匪浅
林赛强:结对项目让我深刻认识到合作与沟通的重要性。与伙伴的紧密配合是项目成功的关键。通过频繁的交流,我们能够及时发现问题、解决问题,并确保双方对任务的理解一致

posted @ 2025-03-22 17:55  一天赚一个亿  阅读(18)  评论(0)    收藏  举报