四则运算

合作者:欧阳雨祥、巫杰龙

github 地址:https://github.com/1471104698/szys

 题目:实现一个自动生成小学四则运算题目的命令行程序(也可以用图像界面,具有相似功能)。

    • 说明:
      自然数:0, 1, 2, …。
      真分数:1/2, 1/3, 2/3, 1/4, 1’1/2, …。
      运算符:+, −, ×, ÷。
      括号:(, )。
      等号:=。
      分隔符:空格(用于四则运算符和等号前后)。
      算术表达式:
      e = n | e1 + e2 | e1 − e2 | e1 × e2 | e1 ÷ e2 | (e),
      其中e, e1和e2为表达式,n为自然数或真分数。
      四则运算题目:e = ,其中e为算术表达式

PSP

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

流程图:

 

 

 

 

代码实现:

主操作类:

主要是输入指令,然后根据指令进行不同的处理

比如确定了 数据的范围 range 和 题目的数量 questions,那么就会进行题目生成,并生成题目文件 和 答案文件

package cn.oy.szys;

import java.io.IOException;
import java.util.Scanner;

/**
 * 对外开放主类
 */
public class OpenMain {
    //数值范围,默认为 0
    int range = 0;
    //题目数量,默认为 0
    int questions = 0;

    public void test() {

        FileName question = new FileName();

        System.out.println("当前已有的题目文件:");
        //获取所有的题目文件数
        question.getExercisesName();
        //question.get_AnswersName();

        System.out.println("生成题目的个数,示例:-n 10 ");
        System.out.println("题目中数值的范围,示例:-r 10");
        System.out.println("对给定的题目文件和答案文件,判定答案中的对错并进行数量统计,示例:-e <exercisefile>.txt -a <answerfile>.txt");

        while (true) {
            //输入操作指令
            inputOpStrs();
            if(range < 0 || questions < 0){
                System.out.println("参数值必须为正数");
                continue;
            }
            if (range == 0 || questions == 0) {
                if (questions == 0)
                    System.out.println("未输入题目数量");
                else{
                    System.out.println("将生成题目数量为:" + questions);
                }
                if (range == 0)
                    System.out.println("未输入数值范围");
                else{
                    System.out.println("生成的题目的数值范围为:" + range);
                }
            } else {
                new Create(range, questions);//题目生成
                System.out.println("题目生成完毕,题目文件存放在项目目录的 Exercises 文件夹中,答案文件存放在 Answers 文件夹中");
                this.range = 0;               //初始化数组范围和题目数量范围
                this.questions = 0;
                System.out.println("数值范围与题目数量已初始化");
                System.out.println("当前已有的题目文件:");
                question.getExercisesName();
            }

        }
    }
    private void inputOpStrs(){
        // 创建Scanner对象
        Scanner scanner = new Scanner(System.in);

        System.out.print("输入指令:");
        // 获取操作指令
        String opStr = scanner.nextLine();
        //操作指令分割,通过空格分割,变成操作指令字符串
        String[] opStrs = opStr.split("\\s+");

        //根据不同的情况进行处理
        switch (opStrs[0]) {
            case "-n":
                this.questions = Integer.parseInt(opStrs[1]);
                break;
            case "-r":
                this.range = Integer.parseInt(opStrs[1]);
                break;
            case "-e":
                //-e Exercises1.txt -a Answers1.txt
                try {
                    //opStrs[1]是题目文件名,opStrs[3]是答案文件名,FileOperate:文件操作类
                    //根据传入的题目文件名 和 答案文件名 进行 匹配校验
                    new FileOperate().proFile(opStrs[1], opStrs[3], questions);
                    System.out.println("对错判断完毕,请在 Finally 文件夹中查看");
                } catch (IOException e) {
                    e.printStackTrace();
                }
                break;
            default: break;
        }
    }
}

 

表示式存储类:

存储表达式,并根据表达式计算出结果(这里只放一部分)

class Exercise {
    int value;  //数的和,单个数的时候是本身,两个数的时候是两数的运算结果
    int value1; //两个数的时候第一个数
    int value2; //两个数的时候第二个数
    int sign = 0;//符号标志,默认为 0,整数
    String createExercise; //createExercise 用来存放算式
    String fraction; //fraction 用来存放分数结果
    char symbol;
    boolean swap = false;                            //是否出现负数需要调换
    boolean isFraction = false;                        //是否是分式
    char[] symbols = new char[]{'+', '-', '*', '/'};

    public Exercise(int range) {
        if ((Math.random() * 2) <= 1) {            //只有一数的情况
            value = (int) (Math.random() * (range - 1) + 1);
            createExercise = String.valueOf(value);
            symbol = '\0';
        } else {
            //两个数的情况
            value1 = (int) (Math.random() * range + 1);
            value2 = (int) (Math.random() * range + 1);
            //随机选取一个符号 + - * /
            symbol = symbols[(int) (Math.random() * 4)];

            //如果是 / ,那么特殊处理
            if (symbol == '/' && value1 % value2 != 0) {
                isFraction = true;
                fraction = proFraction(value1, value2);
            } else {
                value = getValue(value1, value2, symbol);
            }
            if (swap) {
                value = -value;
                createExercise = "(" + value2 + symbol + value1 + ")";
            } else {
                createExercise = "(" + value1 + symbol + value2 + ")";
            }
        }
    }

    public int getValue(int a, int b, char symbol) {
        switch (symbol) {
            case '+':
                return a + b;
            case '-':
                if (a < b)
                    swap = true;
                return a - b;
            case '/':
                return a / b;
            case '*':
                return a * b;
        }
        return 0;
    }

    /**
     * 计算结果,得到表达式的答案
     *
     * @param a
     * @param b
     * @param c
     * @param d
     * @param symbol
     * @return
     */
    public String getAnswer(int a, int b, int c, int d, char symbol) {
        int molecule;
        int denominator;
        switch (symbol) {
            case '+':
                molecule = a * d + b * c;
                denominator = b * d;
                return proFraction(molecule, denominator);
            case '-':
                molecule = a * d - b * c;
                denominator = b * d;
                if (molecule < 0) {
                    this.sign = 1;
                    return proFraction(-molecule, denominator);
                }
                return proFraction(molecule, denominator);

            case '/':
                molecule = a * d;
                denominator = b * c;
                return proFraction(molecule, denominator);
            case '*':
                molecule = a * c;
                denominator = b * d;
                return proFraction(molecule, denominator);
        }
        return null;
    }

    /**
     * 处理分数
     *
     * @param molecule    分子
     * @param denominator 分母
     * @return
     */
    public String proFraction(int molecule, int denominator) {
        //
        int merchant = molecule / denominator;
        //将分数化为真分数,那么需要将分子减去多余的分母的倍数
        molecule -= merchant * denominator;

        //得到分子和分母的最大公约数
        int gcdNum = gcd(molecule, denominator);

        //存储化简分数结果
        String res = "";

        //分子为 0,那么结果直接就是 商
        if (molecule == 0) {
            res = String.valueOf(0);
        } else {
            //分子不为 0,那么根据商的结果判断是真分数还是假分数,对应做处理
            res = String.valueOf(molecule / gcdNum) + '/' + denominator / gcdNum;

            //如果商不为 0,那么我们将商的结果也加到结果字符串中
            if (merchant != 0) {
                res = String.valueOf(merchant) + '’' + res;
            }
        }
        return res;
    }

    private int gcd(int a, int b) {
        if (a % b == 0) {
            return b;
        }
        return gcd(b, a % b);
    }
}

 

文件操作类

用于传入题目和给定答案的匹配校验,基本看注释就知道流程

package cn.oy.szys;

import cn.oy.util.IOUtils;
import cn.oy.constant.IOConstant;
import cn.oy.constant.StringConstant;

import java.io.*;
import java.util.*;

/**
 * 对传入的答案 和 题目 文件进行批改处理
 */
public class FileOperate {//传式子跟答案进文件

    String suffix = StringConstant.FILE_SUFFIX;

    //将题目文件 和 答案文件 进行匹配校验,并写入 Grade 文件中
    public FileOperate()  {
    }

    /**
     * 生成 题目文件和 答案文件
     * @param exercises
     * @param answers
     * @param exercisesName
     * @param answersName
     * @param num
     */
    public void doCreate(String[] exercises, String[] answers, String exercisesName, String answersName, int num){
        String  line = "\n";
        try {
            //获取题目和答案字节流

            BufferedWriter writer1 = (BufferedWriter) IOUtils.getIO(StringConstant.EXERCISES_PATH + exercisesName, IOConstant.WRITER.name());

            BufferedWriter writer2 = (BufferedWriter) IOUtils.getIO(StringConstant.ANSWERS_PATH + answersName, IOConstant.WRITER.name());

            //将表达式写入到对应的文件中
            for (int i = 1; i <= num; i++) {
                writer1.write(i + "、" + exercises[i - 1] + "=" + line);
                writer1.flush();
                writer2.write(i + "、" + exercises[i - 1] + "=" + answers[i - 1] + line);
                writer2.flush();
            }
            //关闭流
            IOUtils.closeIO(writer1, writer2);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 题目 和 答案进行校对,并将结果输入到 Finally 文件中
     * @param exercisesName
     * @param answersName
     * @throws IOException
     */
    public void proFile(String exercisesName, String answersName, int questions) throws IOException{
        //题目序号
        int idx = 1;
        //正确题数
        int correctCount = 0;
        //错误题数
        int wrongCount = 0;

        if(questions <= 0){
            questions = 10000;
        }
        //存储正确题目的标号(从 0 开始计算,偏移量为 1)
        String[] corrects = new String[questions];
        //存储错误题目的标号
        String[] wrongs = new String[questions];

        //获取题目文件的序号
        int index = exercisesName.indexOf(".");
        char num = exercisesName.charAt(index - 1);

        //获取官方 Answers.txt 路径 + 文件名
        String officialAnswerFile =  StringConstant.ANSWERS_PATH + StringConstant.ANSWERS_PREFIXX + num + suffix;
        //获取给定 Answers.txt 路径 + 文件名
        String givenAnswerFile = StringConstant.ANSWERS_PATH + answersName;

        //获取官方答案文件字节流
        BufferedReader reader1 = (BufferedReader) IOUtils.getIO( officialAnswerFile, IOConstant.READER.name());
        //获取给定答案文件字节流
        BufferedReader reader2 = (BufferedReader) IOUtils.getIO(givenAnswerFile, IOConstant.READER.name());

        String str1 = "";
        String str2 = "";

        //将官方答案和给定答案进行匹配校验
        while ((str1 = reader1.readLine()) != null && (str2 = reader2.readLine()) != null) {
            if (str1.equals(str2) && !str1.equals("\n")) {
                corrects[correctCount++] = String.valueOf(idx);
            } else {
                wrongs[wrongCount++] = String.valueOf(idx);
            }
            idx++;
        }
        //关闭流
        IOUtils.closeIO(reader1,reader2);

        //记录正确题号和错误题号到文件中
        inputFinally(correctCount, wrongCount, corrects, wrongs);

    }

    /**
     * 将正确的题号 和 错误的题号 输入到 Finally.txt 文件中
     * @param correctCount
     * @param wrongCount
     * @param corrects
     * @param wrongs
     * @throws IOException
     */
    private void inputFinally(int correctCount, int wrongCount, String[] corrects, String[] wrongs) throws IOException {
        String left = "(";
        String right = ")";
        String line = "\n";
        String comma = ",";
        String correctName = "Correct:";
        String wrongName = "Wrong:";

        //获取字节流
        BufferedWriter writer = (BufferedWriter) IOUtils.getIO(
                StringConstant.FINALLY_PATH + StringConstant.FINALLY_PREFIXX + suffix, IOConstant.WRITER.name());

        //输入正确的题号
        writer.write(correctName + correctCount + left);
        writer.flush();

        for (int x = 0; x < correctCount; x++) {
            if (x != correctCount - 1)
                writer.write(corrects[x] + comma);
            else
                writer.write(corrects[x]);
            writer.flush();
        }

        //输入错误的题号
        writer.write(right + line + wrongName + wrongCount + left);
        writer.flush();

        for (int x = 0; x < wrongCount; x++) {
            if (x != wrongCount - 1)
                writer.write(wrongs[x] + comma);
            else
                writer.write(wrongs[x]);
            writer.flush();
        }
        writer.write(right + line);
        writer.flush();
        //流的关闭
        IOUtils.closeIO(writer);
    }
}

 

 

测试截图

 

 

 

 

 

 总结:

这次项目花费了很多的时间,刚开始看到项目是有思路的,只是不知道从哪里动手写

经过两个人一段时间的协商,做好分工才进动手写代码

对于部分难题,的确卡了不少时间,但综合两个的想法,讨论过后还是能够解决,虽然并不完美,但也是做到了

这次的结对,让我们明白了事前讨论、协商、分工的合理性和必要性,更让我们学会了如何去解决一些之前从未碰过的难题

 

posted @ 2020-04-15 01:04  147110  阅读(294)  评论(0)    收藏  举报