结对作业

这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience
这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience/homework/13479
这个作业的目标 实现一个自动生成小学四则运算题目的程序;体验结对完成项目的过程并进行反思总结
github链接 https://github.com/yasinalong/Calculation-problem-generation.git
成员一 黄昌龙 3123004077
成员二 刘江浩 3123004752

一、PSP表格

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

二、效能分析

image
文件I/O是最大瓶颈(32.68%)
字符串操作开销大(9.87%)
问题生成逻辑(8.35%)
主要还是文件 IO 的性能

三、设计实现过程

1. 核心类设计

程序包含4个核心类,分别负责不同的功能模块。

  • Fraction 类用于处理分数的表示、运算和格式化,其属性包括 numerator(分子)和 denominator(分母),方法有四则运算重载、toString ()(分数格式化)以及 reduce ()(约分)。
  • ExpressionNode 类是表达式树的节点,用于构建抽象语法树(AST),属性包括 type(节点类型:数字 / 运算符)、value(分数值)、op(运算符)以及 left/right(子节点)。
  • Expression 类负责管理表达式树的解析、求值、规范化和字符串转换,方法有 evaluate ()(递归求值)、normalize ()(表达式去重规范化)和 toString ()(表达式格式化)。
  • ProblemGenerator 类用于生成不重复的四则运算题目,属性包括 range(数值范围)和 generatedExpressions(去重集合),方法有 generateProblem ()(生成题目)和 generateExpressionTree ()(生成表达式树)。
  • AnswerChecker 是校验用户答案与标准答案一致性的静态类,其静态方法 checkAnswers () 用于对比题目文件和答案文件,并输出正确率。

2. 类之间的关系

  • 依赖关系:
    Expression 依赖 ExpressionNode(通过指针管理表达式树节点)。
    ExpressionNode 依赖 Fraction(存储数字节点的数值)。
    ProblemGenerator 依赖 Expression 和 ExpressionNode(生成表达式并封装为 Expression 对象)。
    AnswerChecker 依赖 Expression(解析题目表达式并计算标准答案)。
  • 组合关系:
    Expression 包含 ExpressionNode 指针(root),表示一个完整的表达式树。

3. 关键函数流程分析

  • 表达式生成流程(ProblemGenerator::generateProblem())
    image
  • 表达式解析与求值流程(Expression::Expression(const string&) 和 evaluate())
    image
  • 答案校验流程(AnswerChecker::checkAnswers())
    image

四、代码说明

在源文件的头文件中注释的非常的清晰了
最重要的部分就是表达式生成类了
image
这个类实现了,表达式的存储,存储表达树的数据结构是树

五、测试运行

  1. 测试分数类(是不可以创造分母为0的数的,如果创造了分母为0也是默认为1)
    ftest.cpp
#include "head.h"
int main()
{
    Fraction a(1,3),b(5,2),c("1/3"),d("2'2/3"),e("3");
    std::cout << a.toString()<< std::endl;
    std::cout << b.toString()<< std::endl;
    std::cout << c.toString()<< std::endl;
    std::cout << d.toString()<< std::endl;
    std::cout << e.toString()<< std::endl;
     
    return 0;
}

image
2. 测试表达式函数
表达式类测试

#include"head.h"
using namespace std;
int main()
{
    ProblemGenerator h(10);
    for(int i=0;i<10;i++)
    {
       auto [e,f] = h.generateProblem();
       std::cout << e.toString() << "=" << f.toString() <<std::endl;
    }
    return 0;
}

image
expression函数功能测试

int main()
{
    /*创建一棵树*/
    /*
                            root
                    root1           roo2
                leaf1   leaf2   leaf3   leaf4
    */
    auto leaf1 =  make_shared<ExpressionNode> (Fraction(1));
    auto leaf2 =  make_shared<ExpressionNode> (Fraction(2));
    auto root1 =  make_shared<ExpressionNode> ('+',leaf1,leaf2);

    auto leaf3 =  make_shared<ExpressionNode> (Fraction(3));
    auto leaf4 =  make_shared<ExpressionNode> (Fraction(4));
    auto root2 =  make_shared<ExpressionNode> ('+',leaf3,leaf4);

    auto root = make_shared<ExpressionNode>('+',root1,root2);

    Expression e(root);
    std::cout << "结果:" << e.evaluate().toString() << std::endl;
    std::cout << "表达式(带括号):" << e.toString() << std::endl;
    std::cout << "表达式(不带括号:" << e.toNormalizedString() << std::endl;

    return 0;
}

image
3. 测试3
测试去重功能
image

class ProblemGeneratorText
{
public:
    void printExrHash(ProblemGenerator& obj,Expression exr)
    {
      
        std::cout<<"表达式(未整理前):"<< exr.toString()<<std::endl;
        exr.normalize();
        std::cout<<"表达式(整理后):"<<exr.toString()<<std::endl;
        
        std::string str = exr.toNormalizedString();
        if(obj.generatedExpressions.find(str) != obj.generatedExpressions.end())//找得到
            std::cout<<"已经存在了这个表达式"<<std::endl;
        else
        {
            std::cout<<"加入表达式:"<<std::endl;   
            obj.generatedExpressions.insert(str);
        }
    }
};
int main()
{
    /*创建一棵树*/
    /*
                            root
                    root1           leaf3
                leaf1   leaf2   
    */
    auto leaf1F =  make_shared<ExpressionNode> (Fraction(1));
    auto leaf2F =  make_shared<ExpressionNode> (Fraction(2));
    auto root1F =  make_shared<ExpressionNode> ('+',leaf1F,leaf2F);

    auto leaf3F =  make_shared<ExpressionNode> (Fraction(3));

    auto rootF = make_shared<ExpressionNode>('+',root1F,leaf3F);

    Expression e1(rootF);

    auto leaf1L =  make_shared<ExpressionNode> (Fraction(2));
    auto leaf2L =  make_shared<ExpressionNode> (Fraction(1));
    auto root1L =  make_shared<ExpressionNode> ('+',leaf1L,leaf2L);

    auto leaf3L =  make_shared<ExpressionNode> (Fraction(3));

    auto rootL = make_shared<ExpressionNode>('+',root1L,leaf3F);

    Expression e2(rootL);

    ProblemGeneratorText t;

    ProblemGenerator h(10);
    t.printExrHash(h,e1);
    t.printExrHash(h,e2);


    return 0;
}

image
image

六、性能优化

1.优化 io

优化思路,减少文件io
image

但是效果其实并不明显

优化前
image

优化后
image

基本上差异来源于波动,因此根本就没有必要在文件io上苦费时间了
因为这个时候操作系统和库函数已经对文件io进行了优化,设置了缓冲区,但是确实性能开销中文件 io 是消耗最多性能的

2.编译优化

性能直接提升了3倍多(在100000道题目的前提下)
image

3.多线程优化

由于现在的性能已经可以足够了,所以就没有实现多线程优化,因为这样会是程序更加复杂,但是这里也是抛出优化的方案

七、项目小结

本次结对项目不仅完成了功能开发,更让我们体会到协作的价值:通过优势互补提高代码质量,通过思想碰撞解决技术难题。未来项目中,我们将保留每日同步、代码评审等协作机制,同时改进异常处理和性能优化,让程序更健壮、更易用。结对开发不是简单的任务分配,而是共同对项目负责——这种责任感,正是团队开发的核心意义。

posted @ 2025-10-22 18:49  lo0ng  阅读(5)  评论(0)    收藏  举报