结对编程(java)

结对成员 3117004646 陈浩民

     3117004676 叶剑庭

一、Github项目地址:https://github.com/silakami/Myapp.exe



二、PSP表格

Planning 计划 40 35
· Estimate · 估计这个任务需要多少时间 40 35
Development 开发  1480  1585
· Analysis · 需求分析  150 150
· Design Spec · 生成设计文档 80 65
· Design Review · 设计复审  60 70
· Coding Standard · 代码规范 30 30
· Design · 具体设计 90 90
· Coding · 具体编码 900  1000
· Code Review · 代码复审  80  90
· Test · 测试(自我测试,修改代码,提交修改)  90  90
Reporting 报告  150  150
· Test Report · 测试报告  70  70
· Size Measurement · 计算工作量  40  40
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划  40 40 
合计    1670  1770


三、效能分析

在优化之前,生成10000条算式的时间:

优化之后,生成10000条算式的时间

优化内容:

  在原来生成题目中,括号的数量是通过随机生成得到的,但必须要生成该数量的括号,由于是随机生成,所以可能需要很多次都生成出相同的或者是位置不正确的括号,需要删掉重新再生成,后来将括号生成改成了同样是随机生成,但是不需要一定生成这个数量的括号,一旦生成失败就跳过,直到循环结束,这样能使循环的次数减少,提升运行速度,但是降低了括号嵌套的概率。



四、设计实现过程

流程图

对于这个题目,我们将其分成了四个类:Main类,FileOperation类,GenerateTitle类,CheckResult类。

1、Main类:用正则表达式对输入进行检测,接收用户输入。

2、FileOperation类:对文件进行读写操作,并且对读入的文件进行答案校对。

3、GenerateTitle类:使用随机数,生成题目,并且对其添加括号和查重。

4、CheckResult类:专门负责计算题目的答案。



五、代码说明

1、GenerateTitle类

这部分最主要是插入括号和查重,其他生成操作数和操作符是通过随机数生成,然后将它们放到两个数组中,这样方便构建算式和查重,其他生成随机数,真分数都不难,化简只需要求出最大公因数,然后约分就行了。

括号方法,我们的思路是不管怎样先生成随机生成左括号,然后再随机生成右括号。在生成左括号时,要判断这个地方是否已经生成过左括号了,这里就要分两类讨论:①此处没有生成过左括号,则在生成右括号时,需要判断生成右括号的位置是否存在左括号或者左括号位置是否有右括号,如果有,则需要重新生成右括号。②此处已经生成过左括号,则在生成右括号时,需要判断该位置是否有右括号,如果有,那肯定是两个括号括住同一条式子,则需要重新生成,最后还是要判断生成右括号的位置是否存在左括号或者左括号位置是否有右括号。这样才能保证括号的正确加入。

但是这里有个缺陷,这里生成的括号不一定有意义,如1+(2+3),我们没有做到将这种没有意义的括号去除方法,因为要保证不能把程序写死,要保留扩展性,这样做这个确实很难,但是如果能做完,查重就变得简单很多了。

/**
     * 生成括号
     * @param num   操作数数组
     * @param op    操作符数组
     * @param operatorNum   操作数个数
     * @return
     */
    public String Cover(String[] num, String[] op,int operatorNum){
        String title = null;
        int i, j, k, judge, lPoint, rPoint,midel,judge_left,judge_right;
        Random ra = new Random();
        if(operatorNum == 1)
            title = num[0] + " " +op[0] + " " + num[1];     //只有一个操作符,不能生成括号
        else{
            k = ra.nextInt(operatorNum);    //随机生成括号数量
            if(k >= operatorNum)            //括号数量需要小于等于操作符数量减1,否则括号生成位置会出错
                k = ra.nextInt(operatorNum);
            if(k == 0) {
                //不生成括号,直接组装算式
                title = num[0];
                for (i = 1; i < operatorNum + 1; i++)
                    title = title + " " + op[i - 1] + " " + num[i];
            }
            else{
                //采用两个数组,长度为操作数个数,用来标志左右括号的位置
                int[] leftBracket = new int[operatorNum + 1];           //左括号,元素值为该位置左括号数量
                int[] rightBracket = new int[operatorNum + 1];          //右括号,元素值为该位置右括号数量
                for(i = 0;i < operatorNum+1;i++) {
                    //需要赋初值
                    leftBracket[i] = 0;
                    rightBracket[i] = 0;
                }
                for(i = 0;i < k;i++){                       //生成括号
                    lPoint = ra.nextInt(operatorNum);       //随机生成左括号位置
                    judge = 0;                              //标志作用
                    //判断该位置是否有左括号
                    if(leftBracket[lPoint] == 0) {
                        midel = 0;                           //标志该位置没有左括号
                        leftBracket[lPoint]++;
                    }
                    else{
                        midel = 1;                          //标志该位置存在左括号
                        leftBracket[lPoint]++;
                    }
                    rPoint = ra.nextInt(operatorNum) + 1;   //随机生成右括号
                    while(rPoint <= lPoint || (lPoint == 0 && rPoint == operatorNum))
                        //该右括号不能在左括号左边,且不能和左括号在同一位置
                        rPoint = ra.nextInt(operatorNum) + 1;
                    if(midel == 0){                     //该位置上没有左括号
                        if(leftBracket[rPoint] != 0) {  //判断在右括号的位置上是否存在左括号
                            while (leftBracket[rPoint] != 0 || (lPoint == 0 && rPoint == operatorNum) || rPoint <= lPoint) {
                                //随机再产生右括号,若当跳出循环前都没有合适的右括号,则去掉左括号
                                rPoint = ra.nextInt(operatorNum) + 1;
                                judge++;                                    //防止出现死循环
                                if(judge == operatorNum)                    //跳出循环
                                    break;
                            }
                            if(rightBracket[lPoint] != 0 || leftBracket[rPoint] != 0 || judge == operatorNum) {
                                //避免先生成左括号再生成右括号或者反过来生成的情况,即左括号位置有右括号或者右括号位置有左括号,如(3+(4)+5+6)
                                leftBracket[lPoint]--;          //去掉该左括号
                                continue;
                            }
                            rightBracket[rPoint]++;             //添加右括号
                        }
                        else
                            rightBracket[rPoint]++;             //添加右括号
                    }
                    else{                   //在生成左括号位置已经有左括号
                        if(rightBracket[rPoint] != 0) {         //右括号位置已经存在右括号,这种情况不合法,需要重新生成,如((3+4))+5
                            while (rightBracket[rPoint] != 0 || (lPoint == 0 && rPoint == operatorNum) || rPoint <= lPoint) {
                                rPoint = ra.nextInt(operatorNum) + 1;
                                judge++;                        //防止死循环
                                if(judge == operatorNum)
                                    break;
                            }
                            if(rightBracket[lPoint] != 0 || leftBracket[rPoint] != 0 || judge == operatorNum) {
                                leftBracket[lPoint]--;
                                continue;
                            }
                            rightBracket[rPoint]++;
                        }
                        else
                            rightBracket[rPoint]++;
                    }
                    judge_left = 0;
                    judge_right = 0;
                    //由于为了避免增加复杂情况来进行判断,不同情况用不同的生成方法(从内向外,从外向内)
                    //这样减少了括号嵌套的概率,但使代码变简单
                    for(j = lPoint;j <= rPoint;j++){
                        if(leftBracket[j]!=0)
                            //遇到左括号,加该位置左括号个数
                            judge_left+=leftBracket[j];
                        if(rightBracket[j]!=0)
                            //加右括号
                            judge_right+=rightBracket[j];
                    }
                    if(judge_left!=judge_right){
                        //如果左括号数量不等于右括号,则不生成括号
                        rightBracket[rPoint]--;
                        leftBracket[lPoint]--;
                        continue;
                    }
                    //添加括号
                    num[lPoint] = "(" + num[lPoint];
                    num[rPoint] = num[rPoint] + ")";
                }
            }
            title = num[0];
            for(i = 1;i < operatorNum+1;i++)
                title = title + " " + op[i - 1] + " " + num[i];
            return title;
        }
        return title;
    }

查重方法

为何这里要注释掉呢,因为这个查重方法实在是太糟糕了,我们没有想到更优的算法来实现查重,只能够用最白痴的算法来做,它的复杂度是O(n^2),实际上算上外循环是O(n^3),这效率不敢想象,我们生成10000道题需要50s的时间才能生成完,如果没有优化这个算法就不能实际用在我们的程序中,这就是没用数据结构的悲哀。但是在查重时还需要考虑很多效率问题,当-r参数很大的时候,基本上是不会出现重复的,但是-n大于一定值的时候,就有很大可能或者一定会出现重复,我们需要花很大的精力去重新生成一个不重复的式子,甚至进入死循环,这两种情况相当影响算法的效率。

/*
            else {
                //检查重复的算式
                checkCopy = "no";
                if(judgement == "yes" && i != 0)
                    for(k = 0;k < i;k++){
                        checkCop = answer[k].split(Integer.toString(k+1)+". ");
                        if(checkCop[1].equals(checkOut)){
                        //减少检测数量,检测答案相同的式子
                            checkCop = title[k].split(Integer.toString(k+1)+". ");
                            w = 0;
                            //检测长度相同的式子
                            if(!checkCop[1].contains(num[0]) && checkCop[1].split(" ").length == (operationNumber + operatorNum))
                                continue;
                            while(w < operatorNum){
                            //检测式子内部内容
                                if(!checkCop[1].contains(num[w+1]))
                                    break;
                                else if(!checkCop[1].contains(operator[w]))
                                    break;
                                w++;
                            }
                            checkCopy = "yes";
                            break;
                        }
                        else
                            checkCopy = "no";
                    }
                if(checkCopy == "no") {
                    answer[i] = Integer.toString(i + 1) + ". " + checkOut;
                    title[i] = Integer.toString(i + 1) + ". " + title[i];
                }
                else
                    i--;
            }*/

2、CheckResult类

这个类和GenetateTitle类是这个程序的两大核心,而这个类的核心是计算,计算的方法也是相当经典,使用后缀表达式来计算,实际上这个算法思路并不难,关键是具体实现的方法问题,这里我们用了一个技巧,将自然数都当成分数(分母为1)来计算,这样极大地方便程序的设计。

/**
     * 采用后缀表达式计算生成的式子,具体思路参照逆波兰式算法
     * @param str   题目
     * @return
     */
    public String Calc(String str){
        GenerateTitle gen = new GenerateTitle();
        char[] title = str.toCharArray();
        Stack<Character> operator = new Stack<Character>();         //符号栈
        operator.push(‘k‘);                          //压入标志位‘k‘
        Stack<String> number = new Stack<String>();              //操作数栈
        number.push("q");                            //压入标志位"q"
        String molecule1, molecule2, denominator1, denominator2;
        String molecule, midel, denominator;
        char operating;
        String[] result;
        String[] finResult = new String[2];
        int judgeXie, judgeFen, divisor;
        outer: for(int i = 0;i < title.length;i++){
            judgeXie = 0;                                   //初始化‘/‘标志
            judgeFen = 0;                                   //初始化‘\‘‘标志
            if(title[i] == ‘(‘)
                //若从题目取出"(",则入运算符栈
                operator.push(title[i]);
            else if(title[i] == ‘)‘){
                /*若从题目取出")",则从操作数栈取出两个操作数,并从运算符栈取出一个运算符,并进行运算
                *运算完的结果重新压入操作数栈,重复以上过程,直到遇到第一个"(",最后将其出栈
                * 计算时一次取两个数,即为操作数的分子和分母
                */
                while(!operator.peek().equals(‘(‘)){
                    denominator2 = number.pop();                    //取第二个操作数分母
                    molecule2 = number.pop();                       //取第二个操作数分子
                    denominator1 = number.pop();                    //取第一个操作数分母
                    molecule1 = number.pop();                       //取第一个操作数分母
                    operating = operator.pop();                     //取操作符
                    result = CalcAndCheck(operating, molecule1, denominator1, molecule2, denominator2);
                    //如果不符合规范(产生负数或除数为0),则返回null,重新生成一个新的算式
                    if(result == null)
                        return null;
                    else{
                        //将计算结果压栈
                        number.push(result[0]);
                        number.push(result[1]);
                    }
                }
                //将左括号出栈
                operator.pop();
            }
            else if(title[i] == ‘+‘ || title[i] == ‘-‘ || title[i] == ‘ב || title[i] == ‘÷‘){
                /*若从题目取出操作符,若栈顶操作符优先级高于该操作符,则从操作数栈取出两个操作数,并从运算符栈取出一个
                 *运算符,并进行运算,运算完的结果重新压入操作数栈,重复以上过程,直到操作符栈的栈顶元素优先级小于从题
                 *目取出的运算符的优先级(不含等于),则将该操作符压入栈,否则直接入栈
                 */
                if(operator.peek()!=‘k‘ && number.peek()!="q"){             //判断栈是否为空
                    operating = operator.peek();                            //取栈顶元素,但不出栈
                    while(operator.peek()!=‘k‘ && number.peek()!="q" && !JudgePriority(operating, title[i])){
                        //做上述操作直到栈空或者操作符优先级高于栈顶操作符
                        denominator2 = number.pop();
                        molecule2 = number.pop();
                        denominator1 = number.pop();
                        molecule1 = number.pop();
                        operating = operator.pop();
                        result = CalcAndCheck(operating, molecule1, denominator1, molecule2, denominator2);
                        if(result == null)
                            return null;
                        else{
                            //将计算结果压栈
                            number.push(result[0]);
                            number.push(result[1]);
                        }
                        operating = operator.peek();
                    }
                }
                //操作符压栈
                operator.push(title[i]);
            }
            else if(title[i] >= ‘0‘ && title[i] <= ‘9‘){
                /*取出操作数,将操作数压栈
                *这里的将自然数当作分数来算,分母为1,这样极大地减少了计算的复杂度
                * 需要区分
                * 若为真分数,则molecule代表分子部分,denominator代表分母部分
                * 若为假分数,则molecule代表整数部分,midel代表分子部分,denominator代表分母部分
                 */
                molecule = midel = denominator = String.valueOf(title[i]);      //取第一个数
                i++;
                if(i == title.length){
                    //如果到达算式末尾,则直接压入栈
                    number.push(molecule);
                    number.push("1");                   //这种情况只能为整数,所以分母压入1
                    continue;
                }
                //取完整的操作数
                while(i < title.length && title[i] != ‘ ‘ && title[i] != ‘)‘){
                    if(title[i] == ‘/‘) {
                        //遇到‘/‘,则为分母,取分母部分
                        judgeXie = 1;
                        i++;
                        denominator = String.valueOf(title[i]);     //取分母的第一位
                        if(i == title.length - 1){      //到达算式末尾
                            i--;
                            if(judgeFen == 1){
                                //判断是否为真分数,如果是,则将需要将带分数转化为假分数,再压栈
                                number.push(String.valueOf(Integer.valueOf(molecule) * Integer.valueOf(denominator) + Integer.valueOf(midel)));
                            }
                            else
                                number.push(molecule);
                            number.push(denominator);
                            break outer;                //下一个循环
                        }
                        i++;
                        continue;
                    }
                    if(title[i] == ‘\‘‘){
                        //遇到‘\‘‘,即为带分数,则取分子部分
                        judgeFen = 1;
                        judgeXie = 2;
                        i++;
                        midel = String.valueOf(title[i]);
                        i++;
                        continue;
                    }
                    if(judgeXie == 0)
                        molecule = molecule + String.valueOf(title[i]);
                    else if(judgeXie == 1)
                        denominator = denominator + String.valueOf(title[i]);
                    else if(judgeFen == 1)
                        midel = midel + String.valueOf(title[i]);
                    i++;
                }
                if(judgeXie == 1){
                    i--;
                    if(judgeFen == 1) {
                        number.push(String.valueOf(Integer.valueOf(molecule) * Integer.valueOf(denominator) + Integer.valueOf(midel)));
                    }
                    else
                        number.push(molecule);
                    number.push(denominator);
                }
                else{
                    i--;
                    number.push(molecule);
                    number.push("1");
                }
            }
        }
        //计算最终结果
        while(operator.peek()!=‘k‘){
            denominator2 = number.pop();
            molecule2 = number.pop();
            denominator1 = number.pop();
            molecule1 = number.pop();
            operating = operator.pop();
            result = CalcAndCheck(operating, molecule1, denominator1, molecule2, denominator2);
            if(result == null)
                //不符合规范,返回null重新生成新的算式
                return null;
            else{
                //将计算结果压栈
                number.push(result[0]);
                number.push(result[1]);
            }
        }
        finResult[1] = number.pop();                //取最终结果分母
        finResult[0] = number.pop();                //取最终结果分子
        if(finResult[1].equals("1"))                //若分母为1,则为整数
            return finResult[0];
        else if(finResult[0].equals("0"))           //若分子为0,则答案为0
            return "0";
        else{
            divisor = gen.CommonDivisor(Integer.valueOf(finResult[0]), Integer.valueOf(finResult[1]));  //计算最大公约数
            //将结果化简和化成分数,并返回
            return gen.ChangePorper(Integer.valueOf(finResult[0]), Integer.valueOf(finResult[1]), divisor);
        }
    }

    /**
     * 判断符号优先级,只有
     * @param befor     符号栈顶的符号
     * @param after     算式的符号
     * @return
     */
    public boolean JudgePriority(char befor, char after){
        if((befor == ‘+‘ || befor == ‘-‘) && (after == ‘ב || after == ‘÷‘))
            return true;
        else if(befor == ‘(‘) {
            //‘(‘标志着边界,不能再往左计算
            return true;
        }
        else {
            return false;
        }
    }

    /**
     * 对操作数进行运算,具体就是简单的两个分数相运算
     * @param operator  操作符
     * @param mole1     操作数1分子
     * @param demo1     操作数1分母
     * @param mole2     操作数2分子
     * @param demo2     操作数2分母
     * @return
     */
    public String[] CalcAndCheck(char operator, String mole1, String demo1, String mole2, String demo2){
        String[] result = new String[2];
        int m1, m2, d1, d2;
        m1 = Integer.valueOf(mole1);
        m2 = Integer.valueOf(mole2);
        d1 = Integer.valueOf(demo1);
        d2 = Integer.valueOf(demo2);
        if(operator == ‘+‘){
            result[0] = String.valueOf(m1 * d2 + m2 * d1);
            result[1] = String.valueOf(d1 * d2);
        }
        else if(operator == ‘-‘){
            if((m1 * d2 - m2 * d1) < 0)
                //判断在计算过程中是否会产生负数
                return null;
            result[0] = String.valueOf(m1 * d2 - m2 * d1);
            result[1] = String.valueOf(d1 * d2);
        }
        else if(operator == ‘ב){
            result[0] = String.valueOf(m1 * m2);
            result[1] = String.valueOf(d1 * d2);
        }
        else{
            if(m2 == 0 || d2 == 0)
                //除数是否为0
                return null;
            result[0] = String.valueOf(m1 * d2);
            result[1] = String.valueOf(d1 * m2);
        }
        return result;
    }

3、FileOperation类

这个类就是基本的IO操作,最主要但也相当简单的是对答案的方法,直接调用CheckResult类的计算方法,然后用equals方法来检测答案是否相同就行了。

/**
     * 写入文件方法,将生成的题目和答案写入文本文件
     * @param title     题目数组
     * @param answer    答案数组
     */
    public void FWriter(String[] title,String[] answer){
        String titleFile = "src\\Exercises.txt";
        String answerFile = "src\\Answers.txt";
        int length = title.length;
        int i = 0;
        try{
            //打开文件输出流
            BufferedWriter tFile = new BufferedWriter(new FileWriter(new File(titleFile)));
            BufferedWriter aFile = new BufferedWriter(new FileWriter(new File(answerFile)));
            while(i < length){
                //用字符流输出
                tFile.write(title[i]);
                aFile.write(answer[i]);
                tFile.newLine();
                aFile.newLine();
                i++;
            }
            tFile.close();
            aFile.close();
        }catch(Exception ex){
            ex.getMessage();
        }
    }

    /**
     * 读取文件方法,读取用户输入的题目和答案文件并进行校对,将成绩输入到Grade文件
     * @param titlePath     题目路径
     * @param answerPath    答案路径
     * @return
     */
    public int FReader(String titlePath, String answerPath){
        String title = null;
        String answer = null;
        String correct = "Correct:(";
        String wrong = "Wrong:(";
        String[] str = new String[2];
        int i = 1,correctNum = 0;
        try{
            //读取文件内容
            BufferedReader titleReader = new BufferedReader(new FileReader(new File(titlePath)));
            BufferedReader answerReader = new BufferedReader(new FileReader(new File(answerPath)));
            while((title = titleReader.readLine()) != null){
                //每读取一行,就进行判断,并进行统计对错数量
                answer = answerReader.readLine();
                if(CheckAnswer(title, answer)){
                    correctNum++;
                    if(correct.equals("Correct:("))
                        correct = correct + Integer.toString(i);
                    else
                        correct = correct + ", " + Integer.toString(i);
                }
                else
                    if(wrong.equals("Wrong:("))
                        wrong = wrong + Integer.toString(i);
                    else
                        wrong = wrong + ", " + Integer.toString(i);
                i++;
            }
            titleReader.close();
            answerReader.close();
            str = correct.split(":");
            correct = str[0] + ":" + Integer.toString(correctNum) + str[1] + ")";
            str = wrong.split(":");
            wrong = str[0] + ":" + Integer.toString(i - correctNum - 1) + str[1] + ")";
            //将成绩写入Grade文档
            BufferedWriter gradeFile = new BufferedWriter(new FileWriter(new File("src\\Grade.txt")));
            gradeFile.write(correct);
            gradeFile.newLine();
            gradeFile.write(wrong);
            gradeFile.close();
            return 1;
        }catch(Exception ex){
            ex.getMessage();
            System.out.println("文件路径不正确");
            return 2;
        }
    }

    /**
     * 对答案方法,将计算题目答案并和用户答案对比
     * @param title     题目
     * @param answer    答案
     * @return
     */
    public boolean CheckAnswer(String title, String answer){
        CheckResult check = new CheckResult();
        String[] str;
        str = title.split("\\. ",2);
        title = str[str.length-1];
        str = answer.split("\\. ",2);
        answer = str[str.length-1];
        if(check.Calc(title).equals(answer))
            return true;
        else
            return false;
    }

4、Main类

检测输入,防止程序在某些地方死掉

public static void main(String[] args) {
        GenerateTitle generateTitle = new GenerateTitle();
        CheckResult checkResult = new CheckResult();
        FileOperation fileOperation = new FileOperation();
        String fileHandle;
        String[] fileSpit;
        System.out.println("-------------------------------------------------------------------");
        System.out.println("小学生四则运算生成器");
        System.out.println("请按格式输入来选择以下功能:");
        System.out.println("Myapp.exe -n x -r y     x为生成题目个数,y为题目中自然数,真分数,真分数分母的范围(x和y大于0)");
        System.out.println("题目和答案分别在Exercises.txt和Answers.txt中生成");
        System.out.println("               ");
        System.out.println("Myapp.exe -e <exercisefile>.txt -a <answerfile>.txt");
        System.out.println("以上两个txt文件分别为想要判定对错的题目和答案");
        System.out.println("               ");
        System.out.println("-q     退出生成器");
        System.out.println("-------------------------------------------------------------------");
        final String FILEMATCHONE = "(Myapp.exe|myapp.exe)(\\s+(-n))(\\s+\\d+)(\\s+(-r))(\\s+\\d+)";
        final String FILEMATCHTWO = "(Myapp.exe|myapp.exe)(\\s+(-e))(\\s+\\S+)(\\s+(-a))(\\s+\\S+)";
        int judge = 0;
        while(true){
            judge = 0;
            Scanner instruct = new Scanner(System.in);
            fileHandle = instruct.nextLine();
            fileSpit = fileHandle.split("\\s+");
            if(fileHandle.equals("-q")){
                System.out.println("谢谢使用");
                break;
            }
            else if(Pattern.matches(FILEMATCHONE, fileHandle)){
                long startTime = System.currentTimeMillis(); //获取开始时间
                judge = generateTitle.creatTitle(Integer.valueOf(fileSpit[2]), Integer.valueOf(fileSpit[4]));
                if(judge == 0)
                    System.out.println("题目答案生成成功");
                else if(judge == 1) {
                    System.out.println("参数错误,n需要大于0");
                    System.out.println("请重新输入,格式为:");
                    System.out.println("Myapp.exe -n x -r y     x为生成题目个数,y为题目中自然数,真分数,真分数分母的范围(x和y大于0)");
                }
                else {
                    System.out.println("参数错误,r需要大于0");
                    System.out.println("请重新输入,格式为:");
                    System.out.println("Myapp.exe -n x -r y     x为生成题目个数,y为题目中自然数,真分数,真分数分母的范围(x和y大于0)");
                }
                long endTime = System.currentTimeMillis(); //获取结束时间
                System.out.println("程序运行时间:" + (endTime - startTime) + "ms"); //输出程序运行时间
            }
            else if(Pattern.matches(FILEMATCHTWO, fileHandle)){
                judge = fileOperation.FReader(fileSpit[2], fileSpit[4]);
                if(judge == 1)
                    System.out.println("答案校对完毕,成绩已在Grade.txt出生成");
                else{
                    System.out.println("请重新输入正确的文件路径");
                }
            }
            else {
                System.out.println("参数输入错误,请按照格式输入");
                System.out.println("-------------------------------------------------------------------");
                System.out.println("请按格式输入来选择以下功能:");
                System.out.println("Myapp.exe -n x -r y     x为生成题目个数,y为题目中自然数,真分数,真分数分母的范围(x和y大于0)");
                System.out.println("题目和答案分别在Exercises.txt和Answers.txt中生成");
                System.out.println("               ");
                System.out.println("Myapp.exe -e <exercisefile>.txt -a <answerfile>.txt");
                System.out.println("以上两个txt文件分别为想要判定对错的题目和答案");
                System.out.println("               ");
                System.out.println("-q     退出生成器");
            }
            System.out.println("-------------------------------------------------------------------");
            System.out.println("请输入命令:");
        }
    }


六、测试运行

测试题目

Myapp.exe -n 10000 -r 40

更改(9982,9983,9990,9992,9996,9998,10000)的值,然后检验答案

由此可见,能够检测到错误的答案并进行统计。



七、项目小结

  这次结对项目是我们之间第一次合作,因为彼此能力间的有所差距,所以开发的时间比较长,有一部分时间是在指导对方,虽然不能说编程的能力有明显的增长,但是双方在不断交流中也学到了许多东西,也能够把之前对方的建议用在后面的编程中。结对编程对于扩宽编程思路还是有很大帮助的,在自己想不到或者想到的思路不是很好的时候,对方能够提出一些自己没有想到的思路,或者指出自己存在的问题,这样把思路扩宽了,编程也更有效率,准确率也大大提高了。

原文地址:https://www.cnblogs.com/hxxh/p/11689543.html

时间: 2024-10-01 00:37:39

结对编程(java)的相关文章

结对编程Java实现四则运算(林伯浩 刘育明)

GIthub项目地址:https://github.com/3116004696/ruanjiangongcheng/tree/master/Myapp 项目要求: 实现一个自动生成小学四则运算题目的命令行程序. 1. 使用 -n 参数控制生成题目的个数.(实现) 2. 使用 -r 参数控制题目中数值(自然数.真分数和真分数分母)的范围.(未实现真分数运算) 3. 生成的题目中计算过程不能产生负数.(实现) 4. 生成的题目中如果存在形如e1 ÷ e2的子表达式,那么其结果应是真分数.(未实现)

20165219 2017-2018-2《Java程序设计》结对编程一 第一周总结

20165219 2017-2018-2<Java程序设计>结对编程一 第一周总结 结对对象 20165219王彦博 20165232何彦达 需求分析 实现一个程序,要求: 1 支持整数运算 2 支持多运算符运算 3 支持分数运算,支持真分数运算 3 统计正确率 扩展需求: 设计思路 这是一个需要支持复杂运算的程序,不同于简单的单运算符的四则运算,需要设计多个类来实现运算,比如加减乘除以及平方的运算. nml图(部分) 代码进度 整数计算类 public class IntNumber { p

20172311『Java程序设计』课程 结对编程练习_四则运算第一周阶段总结

20172311『Java程序设计』课程 结对编程练习_四则运算第一周阶段总结 结对伙伴 学号 :20172307 姓名 :黄宇瑭 伙伴第一周博客地址: 需求分析 功能要求 1.自动生成题目 可独立使用(能实现自己编写测试类单独生成题目的功能) 可生成不同等级题目,类似于: 1级题目:2 + 5 =: 10 - 5 = 之类的两个数,一个运算符的题目 2.题目运算(判题) 可独立使用 实现中缀表达式转为后缀表达式并计算 判断用户答题正误,并输出正确结果 3.支持真分数 可独立使用 实现分数算式的

2017-2018-2 1723 『Java程序设计』课程 结对编程练习_四则运算

2017-2018-2 1723 『Java程序设计』课程 结对编程练习_四则运算 组内成员 20172327 马瑞蕃 20172320 李闻洲 20172317 蒋子行 需求分析 自动生成题目 可独立使用(能实现自己编写测试类单独生成题目的功能) 可生成不同等级题目 不同等级的题目应该指的是不同难度的题目,意味着题目的复杂程度随着等级的上升而上升 题目运算(判题) 可独立使用 实现中缀表达式转为后缀表达式并计算 自动生成的题目要先转为后缀表达式,并计算这个后缀表达式 判断用户答题正误,并输出正

20172312『Java程序设计』课程 结对编程练习_四则运算第二周阶段总结

20172312『Java程序设计』课程 结对编程练习_四则运算第二周阶段总结 结对伙伴 学号 :20172315 20172318 姓名 :胡智韬 陆大岳 伙伴第一周博客地址: 对结对伙伴的评价:这俩人一开始各编各的还好,到后面就开始吵,从头吵到尾,陆大胖,胡志汪,还好到最后是把代码敲出来了,不容易不容易. 小组结对编程的照片(QQ群截图) 项目中自己负责的部分 代码的综合整理,错误查找,合并以及博客的撰写. 个人贡献度划分 彭霖:胡智韬:陆大岳=3:3:4 相关过程的截图 生成题目类驱动类的

20172315『Java程序设计』课程 结对编程练习_四则运算第二周阶段总结

相关过程截图 中缀转后缀及后缀计算测试截图 题目生成截图 计算正确率截图 关键代码解释 将中缀表达式转换为后缀表达式并对后缀表达式进行计算 import java.util.Stack; import java.util.regex.Pattern; public class StringToArithmetic { public StringToArithmetic() { } // 将中缀表达式字符串计算得到结果 public static double stringToArithmetic

2017-2018-2 20172310『Java程序设计』课程 结对编程练习_四则运算_第二周

2017-2018-2 20172310『Java程序设计』课程 结对编程练习_四则运算_第二周 博客要求 组内同学需各自发表博客 博客中需包含以下内容: 相关过程截图 关键代码解释 遇到的困难及解决方法 写出项目中自己负责的部分 个人贡献度划分(按百分比给小组各成员划分贡献度,各成员贡献之和为100%) 对结对的小伙伴做出评价给出小伙伴博客链接(重点指出需要改进的地方) 给出项目的码云链接 给出小组结对编程照片 博客内容中如需展示团队的共同成果,请进行说明 如有参考或引用的设计.实现,请进行说

20172325『Java程序设计』课程 结对编程练习_四则运算第三周阶段总结

20172325『Java程序设计』课程 结对编程练习_四则运算第二周阶段总结 结对伙伴 学号:20172306 姓名:刘辰 结对伙伴博客链接 刘辰同学对编程的积极程度很高,并且在编程能力上很不错,有自己的想法并且能够把这个想法具体的实践出来,只是在编程的过程中和同学交流不够,总是在最后不行了才发现问题,很容易造成前功尽弃,希望在编程过程中多沟通,减少不必要的时间精力的浪费. 小组结对编程照片 项目中自己负责的部分 我负责的是两个部分,分别是: 1.利用栈将中缀表达式转后缀表达式: 2.对后缀表

2017-2018-2 1723 『Java程序设计』课程 结对编程练习-四则运算-最后阶段

2017-2018-2 1723 『Java程序设计』课程 结对编程练习-四则运算-最后阶段 最后的一周,时间越来越紧张,因为之前的拖沓和一些事情的耽误,导致了如今的紧张,这一周应该是我们小组效率最高的一周,因为放下了很多事情开始补充一些关于四则运算编程的一些东西. 结对编程的战友 20172316 赵乾宸:负责对于整数出题的编程: 20172319 唐才铭:负责对于分数出题的编程: 20172329 王文彬:负责对于计算和题目去重的编写: 测试为分别进行测试,最后由赵乾宸进行集体测试. 每个人