结对项目

| 这个作业属于哪个课程 | 课程 |
| ---- | ---- | ---- |
| 这个作业要求在哪里 | 要求 |
| 这个作业的目标 | 实现四则运算生成器+结对开发 |
| 项目成员 | 3119005413何子阳 github |
| 项目成员 | 3119005414黄浩 github |

github链接

链接

PSP表格

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

一、效能分析

JProfiler性能分析图

内存使用情况

在执行过程中,内存消耗较大的是检测式子是否重复功能,我们将一个式子的元素(运算数,运算符)放入一个set中进行过滤;分析发现当运算符数量大于2时,重复的概率非常小,因此只对运算符数量为1和2的式子进行重复检测,提升了效率。

二、设计实现过程

项目结构

项目类图

类功能说明

  • Preparement:准备工作类,包含运算数的生成和大小比较,简单运算式的计算
  • simpleFormulaGenerator:简单运算器生成器,生成运算符数量1的运算式
  • ComplexFormulaGenerator:复杂运算器生成器,生成运算符数量大于1的运算式
  • FileGenerator:生成题目和答案文件
  • Compare:将答案文件和用户提交的文件进行比对,统计正确率
  • FileGenerator:生成批改文件

三、代码说明

complicatedGenerate:利用简单表达式生成复杂表达式,不满足条件的表达式舍弃、返回空值,细节处已用注释表明

public static AnswerV2 complicatedGenerate(){
       GenerateHelper g = new GenerateHelper();
       char mark = markSet[g.getMarkType()];
       //生成第一个数,随机决定生成数字或简单表达式
       if(g.getFirstType() == 0){
           String firstNum = fractionGenerator();
           g.expression.append(firstNum);
           g.setFirstRes(firstNum);
       }
       else{
           AnswerV2 result = GenerateV2();
           while (result==null){
               result = GenerateV2();
           }
           //获取符号进行加括号的判断
           g.setFirstMark(result.getMark());
           g.setSecMark(mark);
           g.setFirstRes(result.getResult());
           //加括号的情况:前一表达式为加减而后一表达式为乘除
           if((g.getFirstMark() == '+' || g.getFirstMark() == '-') && (g.getSecMark() == 'x' || g.getSecMark() == '÷'))
               g.expression.append('(').append(result.getExpression()).append(')');
           else g.expression.append(result.getExpression());
       }
       g.expression.append(" "+mark+" ");

       if(g.getFirstMark()!='0') g.setFirstMark(mark);
       else g.setSecMark(mark);
       //生成第二个数
       if(g.getSecType() == 0){
           String secNum = fractionGenerator();
           g.expression.append(secNum);
           g.setSecRes(secNum);
       }
       else{
           AnswerV2 result = GenerateV2();
           while (result==null){
               result = GenerateV2();
           }
           if(g.getSecMark()=='0') g.setSecMark(result.getMark());
           else g.setThrMark(result.getMark());
           g.setSecRes(result.getResult());
           //加括号的情况:前一表达式为加减而后一表达式为乘除
           //或前除后乘
           if(((g.getThrMark() == '+' || g.getThrMark() == '-') && (g.getSecMark() == 'x' || g.getSecMark() == '÷'))||
                   (g.getThrMark()=='x'&&g.getSecMark()=='÷')
           )
               g.expression.append('(').append(result.getExpression()).append(')');
           else g.expression.append(result.getExpression());
       }

       /*总体答案是负数或除数为0则舍弃*/
       if(mark=='-' && !fractionComparator(g.getFirstRes(),g.getSecRes())) return null;
       else if(mark=='÷' && g.getSecRes().equals("0")) return null;

       //对单双运算符的运算式子进行重复审计
       if(g.getFirstType()+g.getSecType()<2){
           Set<String> newSet = new HashSet();
           newSet.add(g.getFirstRes());
           newSet.add(g.getSecRes());
           newSet.add(mark+"");
           if(expressionComparator.contains(newSet)) return null;
           else expressionComparator.add(newSet);
       }

       g.setFinalRes(fractionCalculator(g.getFirstRes(),g.getSecRes(),g.getMarkType()));
       return new AnswerV2(g.expression.toString(),g.getFinalRes(),'0');
   }

fractionCalculator:计算真分数或整数计算的结果

public static String fractionCalculator(String firstNum, String secNum, int markType) {
        String s = "";
        String[] fir = fractionToArray(firstNum);
        String[] sec = fractionToArray(secNum);

        switch (markSet[markType]) {
            case '+':
                s = fractionAddition(fir, sec);
                break;
            case '-':
                s = fractionSubtraction(fir, sec);
                break;
            case 'x':
                s = fractionMultiplication(fir, sec);
                break;
            case '÷':
                s = fractionDivision(fir, sec);
                break;

        }
        return s;
    }

四、测试运行

整体运行结果

  • 主界面

  • 批改界面

  • 生成文件目录:D盘根目录下

  • 生成结果

要求3:生成的题目中计算过程不能产生负数
在生成表达式时进行了值的比较,将结果为负数的式子舍弃

要求4:生成的题目中如果存在形如e1÷ e2的子表达式,那么其结果应是真分数
自行设计了一个分数约分的方法,保证分数都是最简形式或真分数

要求5:每道题目中出现的运算符个数不超过3个

算式生成采用了子表达式组装的形式,若复杂表达式两个操作数都是子表达式则整个表达式含3个运算符;若两个操作数都是数字则表达式含1个运算符;

要求6:不会产生重复的表达式
维护了大一个HashSet用来储存过程中产生的所有表达式,通过HashSet的特性保证表达式不会重复

要求7:生成的题目存入执行程序的当前目录下的Exercises.txt文件,在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件
已满足,在整体情况已展示

要求8:程序应能支持一万道题目的生成

要求9:程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计

单元测试
进行10000道题目的生成,没有问题

五、项目小结

成员1:何子阳
1.这是一次很有收获的项目体验。本次作业的题目容易理解,但实现起来有很多方面需要考虑,各个方面综合在一起之后有一种很大的制约的感觉,终于也是完成了这个项目;
2.在这次项目之中也第一次体验到合作开发,我们将项目按功能分为了几个部分并分配到每个人完成,需要用到对方的功能则调用接口,因此在接口设计上也有了一个新的认识。
成员2:黄浩
本次项目是有我和何子阳同学共同完成的,这次的合作项目中,很感谢队友的帮助,感受到了合作的的重要性,我们从一开始的讨论到分工合作都进行的很顺利,合作的过程不断综合各自的想法进行完善,最终完成了这个项目。

posted @ 2021-10-26 00:25  FrankinDublin  阅读(59)  评论(0编辑  收藏  举报