结对项目:四则运算表达式生成程序

一、Github项目地址

https://github.com/oMIZUCHIo/MyAppProject

结对项目成员:周伟健 3118005079;周锦发 3118005078

二、PSP表格

PSP2.1

Personal Software Process Stages

预估耗时(分钟)

实际耗时(分钟)

Planning

计划

 60

 

· Estimate

· 估计这个任务需要多少时间

 60

 

Development

开发

 1600

 

· Analysis

· 需求分析 (包括学习新技术)

 120

 

· Design Spec

· 生成设计文档

 60

 

· Design Review

· 设计复审 (和同事审核设计文档)

 80

 

· Coding Standard

· 代码规范 (为目前的开发制定合适的规范)

 30

 

· Design

· 具体设计

 70

 

· Coding

· 具体编码

 1000

 

· Code Review

· 代码复审

 120

 

· Test

· 测试(自我测试,修改代码,提交修改)

 120

 

Reporting

报告

 100

 

· Test Report

· 测试报告

 30

 

· Size Measurement

· 计算工作量

 15

 

· Postmortem & Process Improvement Plan

· 事后总结, 并提出过程改进计划

 55

 

合计

 

 1760

 

三、效能分析

 

四、设计实现过程

  • MyUtil类  

    process()方法:对外的调用接口,根据封装的参数对象进行操作;

    createProblem()方法:创建单条的表达式;

    countProblem)方法:对输入的表达式进行计算,;

    createOperatorNum()方法:根据输入的范围随机生成操作数字,用于被createProblem()函数调用;

    count()方法:根据输入的两数及操作符进行只含一个操作符表达式的计算;

    add()方法:加法,用于被count()函数调用;

    sub()方法:减分,用于被count()函数调用;

    muti()方法:乘法,用于被count()函数调用;

    div()方法:除法,用于被count()函数调用;

    converNum()方法:简单转化带分数等;

    simplyResult()方法:化简计算结果为真分数;

  • FileUtil类  

    paramJudge()方法:对输入参数进行简单的判断合法性函数;

    write()方法:输出表达式文件和答案文件函数;

    checkAnswers)方法:比对答案函数;

  • Frame类  

    start()方法:创建可视化窗口进行后续操作;

  

五、代码说明

 创建表达式相关函数:只是简单生成各操作数字及操作符并进行简单的拼接

/**
     * @description 创建表达式
     */
    private String createProblem(Parameter parameter){

        Random random = new Random();

        char[] operatorCharList = new char[]{'+', '-', '*', '÷' , '('};

        int operatorCharScope = 5;  //可使用操作符范围

        String operatorNum;     //操作数

        boolean useLeftBrackets = false;  //是否使用左括号

        int numInBrackets = 0;      //括号包围数字数目

        //生成的式子
        StringBuilder line = new StringBuilder();

        //操作符数目设置为 1-3 个
        int operatorCharNum = 1 + random.nextInt(2);

        for(int i = 0 ; i < operatorCharNum ; i ++){

            //生成操作数
            operatorNum = createOperatorNum(parameter.getScopeLength());

            //生成操作符
            char operatorChar = operatorCharList[random.nextInt(operatorCharScope)];

            if(operatorChar == '('){

                //如果还未使用操作符,则 加入左括号 并 生成新的不为括号的操作符
                if(! useLeftBrackets){

                    operatorChar = operatorCharList[random.nextInt(operatorCharScope - 1)];

                    useLeftBrackets = true; //已使用过左括号

                    numInBrackets ++;

                    line.append('(').append(' ').append(operatorNum).append(' ').append(operatorChar).append(' ');

                    //当已使用左括号 且 括号中数字数目为0时 需更换此时操作符为 新的不为括号的操作符
                }else if(numInBrackets == 0){

                    operatorChar = operatorCharList[random.nextInt(operatorCharScope - 1)];

                    line.append(operatorNum).append(' ').append(operatorChar).append(' ');

                    //当已使用左括号 且 括号中数字数目大于1时
                }else {

                    operatorChar = operatorCharList[random.nextInt(operatorCharScope - 1)];

                    line.append(operatorNum).append(' ').append(')').append(' ').append(operatorChar).append(' ');

                    operatorCharScope = 4;    //可使用操作符范围设为4,之后不会再生成 括号字符
                }
            }
            //操作符不是括号就直接添加对应的式子就行
            line.append(operatorNum).append(' ').append(operatorChar).append(' ');
        }
        operatorNum = createOperatorNum(parameter.getScopeLength());
        line.append(operatorNum);

        //如果使用了左括号 且 可使用操作符范围为 0-4 则说明未使用过 右括号  对左括号进行匹配,需要手动添加
        if(useLeftBrackets && operatorCharScope == 5){

            line.append(' ').append(')');
        }

        String lineStr = line.toString();

        if(lineStr.startsWith("(") && lineStr.endsWith(")")){

            lineStr = lineStr.substring(1 , lineStr.length() - 1);
        }
        return lineStr;
    }

/**
     * @description 生成操作数字
     * @param range 取值范围
     */
    String createOperatorNum(int range){

        Random random = new Random();

        StringBuilder sb = new StringBuilder();

        //生成整数或分数的随机标识,0表示生成整型,1表示生成分数
        int flag = random.nextInt(2);

        if(flag == 0){

            sb.append(random.nextInt(range + 1));
        }else {
            int denominator = 1 + random.nextInt(range); //分母
            int numerator = 1 + random.nextInt(range);   //分子

            //当分子大于分母时
            if(numerator > denominator){

                int intNum = numerator / denominator;   //假分数前的整数
                numerator = numerator % denominator;   //得出新的分子

                //整除
                if(numerator == 0){
                    sb.append(intNum);
                }else{
                    sb.append(intNum).append('\'').append(numerator).append("/").append(denominator);
                }
            }else if(numerator == denominator){

                sb.append(1);
            }else if(denominator == 1){

                sb.append(numerator);
            }else{

                sb.append(numerator).append("/").append(denominator);
            }
        }
        return sb.toString();
    }

 

计算表达式相关函数:因为我们两人都不知道这方面有什么好的算法,所以表达式的计算是按自己做题的方式来写的,所以性能可能会差点

/**
     * @description 计算结果
     * @param problem 问题
     * @return java.lang.String
     */
    String countProblem(String problem){

        List<String> charList = converProblemToList(problem);

        //含有括号,计算括号内容结果,再用结果替换括号
        if(charList.indexOf("(") >= 0){

            int i = charList.indexOf("(");

            int temp = i;

            StringBuilder sb = new StringBuilder();
            i ++;
            while (!charList.get(i).equals(")")){
                sb.append(charList.get(i)).append(" "); //得出括号内的计算式
                charList.remove(i); //删去括号内内容
            }
            charList.remove(i); //删去多余的右括号
            String bracketResult = countProblem(sb.toString()); //计算括号内式子
            if(bracketResult.equals("error")){
                return bracketResult;     //出现负数
            }else if(bracketResult.equals("X")){ //除数出现0
                return bracketResult;
            }
            charList.set(temp,bracketResult);   //用结果替换原来的左括号内容
        }
            //此时已不含括号 , 格式: 1 + 2 * 3 + 8
            //先进行乘除的优先计算
            for(int i = 1 ; i < charList.size() ; ){

                if(charList.get(i).equals("*") || charList.get(i).equals("÷")){
                    String num1 = charList.get(i - 1);
                    String num2 = charList.get(i + 1);
                    String countResult = count(num1,num2,charList.get(i));

                    if(countResult == null){
                        return "X";     //除数出现0
                    }
                    System.out.println("num1 " + num1 + " " + charList.get(i) + " " + "num2 " + num2 + " = " + countResult);

                    charList.set(i - 1,countResult);
                    charList.remove(i + 1);
                    charList.remove(i);

                    System.out.println("size:" + charList.size() + ";" + charList.toString());
                }else{
                    i = i + 2;  //不为乘除号则查询下一个操作符
                }
            }
            //集合大小不为1则说明还未计算完成,此时式中只剩加减号
            if(charList.size() != 1){
                for(int i = 1 ; i < charList.size() ; ){
                    String num1 = charList.get(i - 1);
                    String num2 = charList.get(i + 1);
                    String countResult = count(num1,num2,charList.get(i));
                    if(countResult == null){
                        return "error";     //出现负数
                    }
                    charList.set(i - 1,countResult);
                    charList.remove(i + 1);
                    charList.remove(i);
                }
            }
        return charList.get(0);
    }

/**
     * @description 两数计算
     * @param num1  数1
     * @param num2  数2
     * @param operatorChar 操作符
     * @return java.lang.String
     */
    String count(String num1, String num2, String operatorChar){

        String result;

        int[] result1 = converNum(num1);
        int[] result2 = converNum(num2);

        int numerator1 = result1[0];
        int denominator1 = result1[1];
        int numerator2 = result2[0];
        int denominator2 = result2[1];

        System.out.println(numerator1 + "/" + denominator1 + "  " + numerator2 + "/" + denominator2);

        switch (operatorChar){
            case "+" :
                result = add(numerator1,denominator1,numerator2,denominator2);break;
            case "-" :
                result = sub(numerator1,denominator1,numerator2,denominator2);break;
            case "*" :
                result = muti(numerator1,denominator1,numerator2,denominator2);break;
            case "÷" :
                result = div(numerator1,denominator1,numerator2,denominator2);break;
            default :
                result = null;
        }
        return result;
    }

 

 化简相关:

    /**
     * @description 化简计算结果
     * @param result 结果
     * @return java.lang.String
     */
    String simplyResult(String result){

        if(result.equals("X")){
            return result;  //特殊情况直接返回
        }
        String[] splitResult = result.split("/"); //因为结果必为真分数/或整型,所以直接分割

        //本身结果就是整型
        if(splitResult.length == 1){
            return result;  //不做处理
        }else {

            int numerator = Integer.valueOf(splitResult[0]);    //分子
            int denominator = Integer.valueOf(splitResult[1]);  //分母

            //若分母为1,直接返回分子
            if(denominator == 1){
                return String.valueOf(numerator);
                //若分子为0,直接返回0
            }else if(numerator == 0){
                return "0";
            }else{
                //分母整除分子
                if(denominator % numerator == 0){

                    return 1 + "/" + denominator / numerator;
                }else{

                    int intNum = numerator / denominator;   //假分数前的整数
                    numerator = numerator % denominator;   //得出新的分子
                    //余数为0 整除
                    if(numerator == 0){
                        //返回整除结果
                        return String.valueOf(intNum);
                    }else{
                        if(intNum == 0){

                            return simplyDivision(numerator,denominator);
                        }else{
                            if(denominator % numerator == 0){
                                return intNum + "'" + "1/" + denominator / numerator;
                            }else{
                                return intNum + "'" + simplyDivision(numerator,denominator);
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * @description 化简真分数
     * @param numerator 分子
     * @param denominator 分母
     * @return java.lang.String
     */
    private String simplyDivision(int numerator, int denominator){

        int y = 1;
        for (int i = numerator ; i >= 1; i--) {
            if (numerator % i == 0 && denominator % i == 0) {
                y = i;
                break;
            }
        }
        int z = numerator / y;// 分子
        int m = denominator / y;// 分母
        if (z == 0) {
            return "0";
        }
        if(m == 1){
            return String.valueOf(z);
        }
        return z + "/" + m;
    }

 

文件类相关:采用IO流进行操作

/**
     * @param problemStr    问题
     * @param resultStr     答案
     * @param directoryPath 当前文件夹路径
     * @return java.lang.String
     * @description 将信息写入文件中
     */
    String wirte(String problemStr, String resultStr, String directoryPath) {
        try {
            File problemFile = new File(directoryPath + "\\Exercises.txt");
            boolean flag1 = problemFile.createNewFile();
            File resultFile = new File(directoryPath + "\\Answers.txt");
            boolean flag2 = resultFile.createNewFile();
            if (!(flag1 && flag2)) {
                return "创建文件出错";
            }
            FileOutputStream fos = new FileOutputStream(problemFile.getAbsolutePath());
            fos.write(problemStr.getBytes());

            fos.flush();

            fos = new FileOutputStream(resultFile.getAbsolutePath());
            fos.write(resultStr.getBytes());

            fos.close();
            return null;
        } catch (Exception e) {
            return "文件写入出错,错误原因:" + e.getMessage();
        }
    }

    /**
     * @description 比对答案
     * @param trueAnswersPath  用户答案路径
     * @param myAnswersPath 正确答案路径
     * @param directoryPath 成绩输出文件夹
     * @return java.lang.String
     */
    String checkAnswers(String trueAnswersPath, String myAnswersPath, String directoryPath) {

        try {
            BufferedReader myreader = new BufferedReader(new FileReader(myAnswersPath));
            BufferedReader truereader = new BufferedReader(new FileReader(trueAnswersPath));

            StringBuilder trueResult = new StringBuilder();
            StringBuilder falseResult = new StringBuilder();

            int trueNum = 0;
            int falseNum = 0;

            String[] myStrings;
            String[] trueStrings;

            String myLine = null;    //用户答案逐行
            String trueLine = null; //正确答案逐行

            String myAnswers = null;    //用户答案
            String trueAnswers = null;  //正确答案

            int myIndex = 0;
            int trueIndex = 0;

            while ((myLine = myreader.readLine()) != null) {

                if (myLine.trim().equals("")) {     //跳过空行
                    continue;
                }
                trueLine = truereader.readLine();
                while(trueLine.trim().equals("")){  //跳过空行
                    trueLine = truereader.readLine();
                }
                myStrings = myLine.split(". ");
                trueStrings = trueLine.split(". ");

                myIndex = Integer.valueOf(myStrings[0].trim()); //题目序号
                trueIndex = Integer.valueOf(trueStrings[0].trim());

                while(myIndex > trueIndex){     //使答案序号一致
                    trueLine = truereader.readLine();
                    while(trueLine.trim().equals("")){  //跳过空行
                        trueLine = truereader.readLine();
                    }
                    trueStrings = trueLine.split(". ");
                    trueIndex = Integer.valueOf(trueStrings[0].trim());
                }
                while(myIndex < trueIndex){     //使答案序号一致
                    myLine = myreader.readLine();
                    while(myLine.trim().equals("")){  //跳过空行
                        myLine = myreader.readLine();
                    }
                    myStrings = myLine.split(". ");
                    myIndex = Integer.valueOf(myStrings[0].trim());
                }
                if (myStrings.length == 1) {    //无答案直接算错
                    falseResult.append(myIndex).append(",");
                    falseNum ++;
                    continue;
                }
                myAnswers = myLine.split(". ")[1].trim();        //用户答案
                trueAnswers = trueLine.split(". ")[1].trim();    //正确答案
                if(myAnswers.equals(trueAnswers)){
                    trueResult.append(myIndex).append(",");
                    trueNum ++;
                }else{
                    falseResult.append(myIndex).append(",");
                    falseNum ++;
                }
            }
            myreader.close();

            String trueStr = trueResult.toString();
            String falseStr = falseResult.toString();

            File outPutFile = new File(directoryPath + "\\Grade.txt");
            int i = 1;
            while(outPutFile.exists()){ //防止文件名重复造成的文件创建失败
                outPutFile = new File(directoryPath + "\\Grade(" + i + ").txt");
                i ++;
            }
            boolean flag = outPutFile.createNewFile();
            if (!flag) {
                return "创建成绩输出文件出错";
            }
            FileOutputStream fos = new FileOutputStream(outPutFile.getAbsolutePath());

            String grade = "Correct: " + trueNum + "(" + trueStr.substring(0,trueStr.length() - 1) + ")\n\n"
                    + "Wrong: " + falseNum + "(" + falseStr.substring(0,falseStr.length() - 1) + ")";

            fos.write(grade.getBytes());
            fos.close();
        } catch (Exception e) {
            return "答案格式出错!,每道题格式如:1. 1/2(序号.空格+答案)," + e.getMessage();
        }
        return null;
    }

六、测试运行

 生成10000道题目

                      

修改9995答案及10000题答案,结果:

七、PSP表格(完成)

PSP2.1

Personal Software Process Stages

预估耗时(分钟)

实际耗时(分钟)

Planning

计划

 60

 50

· Estimate

· 估计这个任务需要多少时间

 60

 50

Development

开发

 1600

 1900

· Analysis

· 需求分析 (包括学习新技术)

 120

 200

· Design Spec

· 生成设计文档

 60

 80

· Design Review

· 设计复审 (和同事审核设计文档)

 80

 150

· Coding Standard

· 代码规范 (为目前的开发制定合适的规范)

 30

 40

· Design

· 具体设计

 70

 60

· Coding

· 具体编码

 1000

 1300

· Code Review

· 代码复审

 120

 100

· Test

· 测试(自我测试,修改代码,提交修改)

 120

 150

Reporting

报告

 100

 150

· Test Report

· 测试报告

 30

 50

· Size Measurement

· 计算工作量

 15

 20

· Postmortem & Process Improvement Plan

· 事后总结, 并提出过程改进计划

 55

 80

合计

 

 1760

 2100

八、项目小结

  本次的结对项目作业,对于我们两个人而言都是一个挺好的锻炼机会,我们在项目开始搭建前便一起进行了基础的分工,以及各板块的基本设计方式,这使得我们后面项目的进行相对比较顺利。在这个过程中,我们相互学习各自的优点,一起讨论解决项目中出现的BUG,进行了一次难得的团队开发经历,对我们各自的技能提升都有着一定的帮助。但不可忽视的是,我们在项目进行期间也出现了一些问题,这次项目因为赶时间也没有完成得特别好,像去重的任务我们到现在也没有想到一个好的解决方式...

  周伟健的闪光点:有比较好的代码执行能力,做事有比较不错的想法

  周锦发的闪光点:任劳任怨,善于倾听队友想法,有比较好的解决问题的能力

 

posted @ 2020-04-02 01:23  oMIZUCHIo  阅读(292)  评论(0)    收藏  举报