结对项目——自动生成小学四则运算题目


1.Github项目地址:https://github.com/13202008832/FourArithmetic

作者:陈惠霖  3118005088

     侯晓龙  3118005091

2.PSP表格(时间评估)

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

3.效能分析

 出现问题:因为我们使用的是栈操作,不同类型的经常要调用函数进行转变,又重新转换回去

 解决方案:全部转换为同一类型操作,减少类型转变

 消耗最大函数:表达式生成函数

void Exp(stack S,int I) {
        //提取符号
        String f = new String();
        f = S.pop();
        //定义优先等级
        int i;
        if (f == "+" || f == "-")
            i = 1;
        else i = 2;
        //提取右表达式
        stack b = new stack(7);
        {
            int m = 0, n = 0;
            while(true) {
                String x = S.pop();
                if (x == "+" || x == "-" || x == "×" || x == "÷")
                    n++;
                else 
                    m++;
                b.push(x);
                if(1 == m - n)
                    break;
            }
            b.inversion();
        }
        //提取左表达式
        stack a = new stack(7);    
        while(true) {
            String x = S.pop();
            a.push(x);
            if(S.isEmpty()) break;
        }
        a.inversion();
        //表达式输出
        if(i < I)
            Y = Y + "(";
        if(a.top == 0)
            Y = Y + a.pop();
        else Exp(a,i);
        Y = Y + " "+f+" ";
        if(b.top == 0)
            Y = Y + b.pop();
        else Exp(b,i+1);
        if(i < I)
            Y = Y + ")";
        if(I == 0) Y = Y + " = ";
    }

4.设计实现过程

   通过对题目的分析,我们发现对于分数和整数的表示都可以用分数来表示,于是我们创建了一个Fraction的实体类,并使用Method包下的Rando类中的getFraction(intmax)方法随机获得一个String类型的分数,和getOperator()方法来获取一个随机的操作符,对于表达式的生成,我们采用了逆波兰表达式,并用Entity包下的stack类存储和Method包下的rpn类排序入栈,answer类来实现表达式答案的获取,而对于实现四则运算的加减乘除,我们在Method包下建立了OpFraction类,用 opFr(String m,String op, String n) 方法实现,最后,我们采用的是建立JavaWeb项目来完成这个结对项目,用户可以在jsp建立的页面上控制题目数量和数值范围,再在D盘中3个txt文件中查看题目、答案、答题情况。

5.代码说明

 Fraction实体类代码

package Entity;

import Method.OpFraction;

public class Fraction {

    private int fson;   //分子m
    private int fma;   //分母n
    private String fract;  //分数m/n
    private String daiFract; //带分数i'm/n
    private int dai;  //i
    
    public int getDai() {
        return dai;
    }
    public void setDai(int dai) {
        this.dai = dai;
    }
    public String getDaiFract() {
        return daiFract;
    }
    public void setDaiFract(int i,int m,int n) {
        this.dai=i;
        this.fma=n;
        this.fson=m;
        this.daiFract = String.valueOf(i)+"'"+String.valueOf(m)+"/"+String.valueOf(n);;
    }
    public int getFson() {
        return fson;
    }
    public void setFson(int fson) {
        this.fson = fson;
    }
    public int getFma() {
        return fma;
    }
    public void setFma(int fma) {
        this.fma = fma;
    }
    public String getFract() {
        return fract;
    }
    public void setFract(int m,int n) {
        this.fma=n;
        this.fson=m;
        this.fract = String.valueOf(m)+"/"+String.valueOf(n);
    }
    public String tranFr(Fraction q) {
        int max;
        OpFraction o=new OpFraction();
        
        if(q.fson==0) {return "0";}else {
        max=o.maxNum(q.fma,q.fson);
        q.setDaiFract(q.dai, q.fson/max, q.fma/max);
        if(q.dai==0) {
            if(q.fma==1) {
                return String.valueOf(q.fson);                
            }
            else{
            
             return String.valueOf(q.fson)+"/"+String.valueOf(q.fma);
        }}
        else {
            if(q.fma==1) {
                return String.valueOf(q.fson);                
            }
            else return String.valueOf(q.dai)+"'"+String.valueOf(q.fson)+"/"+String.valueOf(q.fma);
        }
    }}
    
    
    
    public double val(Fraction f) {
    
        if(f.fson==0)return 0;
        return f.dai+f.fson/f.fma;
    }
    
        
}

getFraction(intmax)方法随机获得一个String类型的分数

    public String getFraction(double max) {
        int dai,son,ma;
        Fraction f=new Fraction();
        do {
            dai=getNumber((int) max);
            son=getNumber((int) max);
            ma=getNumber((int) max);
            for(;ma==0;)
                ma=getNumber((int) max);
            f.setDaiFract(dai, son, ma);
        }while(!(f.val(f)ma);
        return f.tranFr(f);
    
    }

 tranFr(Fraction f)将Fraction转换为String形式

    public String tranFr(Fraction q) {
        int max;
        OpFraction o=new OpFraction();
        
        if(q.fson==0) {return "0";}else {
        max=o.maxNum(q.fma,q.fson);
        q.setDaiFract(q.dai, q.fson/max, q.fma/max);
        if(q.dai==0) {
            if(q.fma==1) {
                return String.valueOf(q.fson);                
            }
            else{
            
             return String.valueOf(q.fson)+"/"+String.valueOf(q.fma);
        }}
        else {
            if(q.fma==1) {
                return String.valueOf(q.fson);                
            }
            else return String.valueOf(q.dai)+"'"+String.valueOf(q.fson)+"/"+String.valueOf(q.fma);
        }
    }}

tranString(String str)将String类型用Fraction存储

Fraction tranString(String str) {
        Fraction f=new Fraction();
        if(str.indexOf("'")>=0) {
        String[] s1=str.split("'");
        
        String[] s2=s1[1].split("/");
        f.setDaiFract(Integer.parseInt(s1[0]),Integer.parseInt(s2[0]), Integer.parseInt(s2[1]));
        }
        else {
            if(str.indexOf("/")>=0) {
                String[] s1=str.split("/");
                f.setDaiFract(0,Integer.parseInt(s1[0]), Integer.parseInt(s1[1]));
            }
            else {
                
                f.setDaiFract(0,Integer.parseInt(str),1);
                
            }
        }
        return f;
        
    }

rpn类实现将生成的数和操作符组成随机逆波兰表达式的功能

public class rpn {
    public stack Y = new stack(7);;
    
    public void r(int max) {
        Rando f=new Rando();
        
        Y.push(f.getFraction(max));
        Y.push(f.getFraction(max));
        int m = 0,n = 0,i = (int)(Math.random()*3);//均等生成0、1、2
        while(m < i && n < i) {
            int j = (int)(Math.random()*2);
            if(j == 0 || n-m>0) {Y.push(f.getFraction(max));m++;}
                else {Y.push(f.getOperator());n++;}
        }
        while(m == i && n < i) {
            Y.push(f.getOperator());n++;
        }
        while(n == i && m < i) {
            Y.push(f.getFraction(max));m++;
        }        
        Y.push(f.getOperator());
        
    }
}

expression类实现了转换逆波兰表达式为标准表达式的功能

public class expression {
    public String Y = new String();
    public stack as;
    
    public expression(stack As) {
        this.as=As;
        }
    
    void Exp(stack S,int I) {
        //提取符号
        String f = new String();
        f = S.pop();
        //定义优先等级
        int i;
        if (f == "+" || f == "-")
            i = 1;
        else i = 2;
        //提取右表达式
        stack b = new stack(7);
        {
            int m = 0, n = 0;
            while(true) {
                String x = S.pop();
                if (x == "+" || x == "-" || x == "×" || x == "÷")
                    n++;
                else 
                    m++;
                b.push(x);
                if(1 == m - n)
                    break;
            }
            b.inversion();
        }
        //提取左表达式
        stack a = new stack(7);    
        while(true) {
            String x = S.pop();
            a.push(x);
            if(S.isEmpty()) break;
        }
        a.inversion();
        //表达式输出
        if(i < I)
            Y = Y + "(";
        if(a.top == 0)
            Y = Y + a.pop();
        else Exp(a,i);
        Y = Y + " "+f+" ";
        if(b.top == 0)
            Y = Y + b.pop();
        else Exp(b,i+1);
        if(i < I)
            Y = Y + ")";
        if(I == 0) Y = Y + " = ";
    }
    public void exp() {
        Exp(as,0);
    }
}

answer类实现获取生成好的表达式的答案的功能

public class anwser {
     public String Y = new String();
     public stack as;
     public boolean flag = true;
     
     public anwser(stack As) {
      this.as=As;
         }
     
     public void anw(){
      stack p = new stack(7);
      while(as.top>0) {
       String a = new String();
       String b = new String();
       String f = new String();
       f = as.pop();
       int i = 0;
       //得出一组fab
       while(true) {
        String x;
        x = as.pop();
        if (x == "+" || x == "-" || x == "×" || x == "÷") {
         p.push(f);
         if(i == 1)
          p.push(a);
         f = x;
         i = 0;
        }
        else if(i == 0) {
         a = x;
         i++;
        }
        else {
         b = x;
         i++;
        }
        if(i == 2)break;
       }
       String y = new String();
       OpFraction op=new OpFraction();
       y = op.opFr(b,f,a);
       if(y=="-1")flag=false;
       as.push(y);
       while(!p.isEmpty()) {
        as.push(p.pop());
       }
      }
      Y = as.pop();
     }
}

DaoTxt类实现了将String类型数据写入txt的相关功能

package Method;

import java.io.FileWriter;
import java.io.IOException;

public class DaoTxt {
    private static String answerPath = "D:\\answerfile.txt";
    private static String correctPath = "D:\\Grade.txt";
    private static String tiPath = "D:\\exercisefile.txt";
    private static void saveAsFileWriter(String content,String path) {
        FileWriter fwriter = null;
        try {
            // true表示不覆盖原来的内容,而是加到文件的后面。若要覆盖原来的内容,直接省略这个参数就好
            fwriter = new FileWriter(path, true);
            fwriter.write(content);
        } catch (IOException ex) {
            ex.printStackTrace();
        } finally {
            try {
                fwriter.flush();
                fwriter.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
    public void clear(String path) {
        FileWriter fwriter = null;
        try {
            // true表示不覆盖原来的内容,而是加到文件的后面。若要覆盖原来的内容,直接省略这个参数就好
            fwriter = new FileWriter(path, false);
            fwriter.write("\n");
        } catch (IOException ex) {
            ex.printStackTrace();
        } finally {
            try {
                fwriter.flush();
                fwriter.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
    public void saveAnswer(String content) {
        saveAsFileWriter(content,answerPath);
    }
    public void saveGrade(String content) {
        saveAsFileWriter(content,correctPath);
    }
    public void saveExercise(String content) {
        saveAsFileWriter(content,tiPath);
    }
    public void clearAll() {
        clear(answerPath);
        clear(correctPath);
        clear(tiPath);
    }
}

6.测试说明

 首先在eclipse中启动Tomcat服务(2000与10皆可自己输入)

点击确定,生成题目

点击提交

exercisefile.txt中存放了生成的所有题目

answerfile.txt中存入了所有题目以及答案

Grade.txt中存储了答题情况

7.PSP表格(实际耗费时间)

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

合计

920 960

8.项目小结

 项目总结:本次结对作业进行基本顺利,我们各自负责着自己的模块,虽然线上讨论减少了一些比较明确的沟通,但是通过对方的介绍跟对各自代码的微调,如特定函数的使用说明、格式的规定(我们最终统一将数字和符号改为String类型),使得我们最终顺利对接成功,经过统一整理后才做出这次项目。因此,对于结对项目,我们有以下观点:

  • 合理分配工作,各自选择自己擅长的模块
  • 确定好对接方式,有相应的函数调用说明
  • 找出问题所在,积极与对面沟通解决方案

 结对感受:陈惠霖:侯晓龙是个非常不错的伙伴,他帮我解决了很多我不擅长的模块,提供了我很多函数的调用,也很少有bug的存在,给予了我很多帮助,非常给力!

      侯晓龙:两个人一起合作感觉还不错,只需要自己完成一部分,所以工作时压力不是很大,很多问题经过讨论就会迎刃而解,队友陈惠霖给了我很多帮助,感觉非常不错。

相关