GitHub 地址

一. 项目要求

题目 实现一个自动生成小学四则运算题目的命令行程序。

需求(全部完成)

  1. 使用 -n 参数控制生成题目的个数

    Myapp.exe -n 10
  2. 使用 -r 参数控制题目中数值(自然数、真分数和真分数分母)的范围

    Myapp.exe -r 10
  3. 生成的题目中计算过程不能产生负数,也就是说算术表达式中如果存在形如e1 − e2的子表达式,那么e1 ≥ e2。
  4. 生成的题目中如果存在形如e1 ÷ e2的子表达式,那么其结果应是真分数。
  5. 每道题目中出现的运算符个数不超过3个。
  6. 程序一次运行生成的题目不能重复,即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目。即不能通过有限次变换变成同一道题目,下面例子均为重复题目。
  • 23+45 与 45+23
  • 3+(2+1)与1+2+3

生成的题目存入执行程序的当前目录下的Exercises.txt文件,格式如下:

  1. 四则运算题目 1
  2. 四则运算题目 2

    ……

    真分数五分之三表示为3/5,真分数二又八分之三表示为2’3/8
  1. 在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件,格式如下
  1. 答案1
  2. 答案2

    特别的,真分数的运算如下例所示

    1/6 + 1/8 = 7/24
  1. 程序应能支持一万道题目的生成。
  2. 程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计,结果输出至 Grade.txt

    Myapp.exe -e <exercisefile>.txt -a <answerfile>.txt

    格式如下

Correct: 5 (1, 3, 5, 7, 9)

二. PSP

三. 思路分析

生成题目:

使用随机函数随机决定运算符个数,运算符类型,数值是否为分数(真分数或假分数),是否包含括号等因素;

在生成数值时,统一先使用 apache 类库中的 Fraction 分数类来封装,以方面后面的计算;

每循环生成好一道题目,便调用方法生成答案,生成答案时利用递归的方式一步步算出最终答案,并将题目与答案一同封装进 Question 类中,生成答案时遇到负数,选择跳过重新生成题目;

检查重复题目,会根据题目的运算符的优先计算进行重新排序,并将同一个运算符的两个数值根据大小的关系进行了排序,再进行查重。

核对答案:

这个功能需要读取两个文件,读取题目文件时,计算出题目的正确答案,最后读取答案文件,将两者对比即可。

四. 代码说明

主要的类

  1. Main 启动类
  2. ExerciseService 生成题目与答案 (-n -r 命令)
  3. CheckService 读取答案进行比对 (-e -a 命令)

Main.java 启动函数


public static void main(String[] args) throws IOException {
int questions, range;
//匹配命令 -n -r
if (Arrays.stream(args).anyMatch("-n"::equals) && Arrays.stream(args).anyMatch("-r"::equals)) {
questions = Integer.valueOf(args[1]);
range = Integer.valueOf(args[3]) - 1;
//生成题目
ExerciseSevice.generateExercise(range, questions);
System.out.println("生成题目完毕!");
//匹配命令 -e -a
} else if (Arrays.stream(args).anyMatch("-e"::equals) && Arrays.stream(args).anyMatch("-a"::equals)) {
//核对答案
CheckSevice.checkExercise(args[1], args[3]);
System.out.println("结果生成成功!");
} else {
System.out.println("错误命令!");
}
}

ExerciseService 生成题目和答案

@SuppressWarnings("unchecked")
public class ExerciseSevice {
//存放题目与答案的列表
public static List<Question> questionsList = new ArrayList<>();
//是否是负数
private static boolean isNegative = false;
//按计算顺序记录每步的答案
private static List<Fraction> answerList = new ArrayList<>();
//按计算顺序记录每个操作符
private static List<String> operatorList = new ArrayList<>(); /**
* 生成题目与答案,并写入文件
*
* @param range 取值范围
* @param questions 题目总数
*/
public static void generateExercise(int range, int questions) throws IOException {
//生成题目
ExerciseSevice.getQuestion(questions, range);
BufferedWriter exerciseWriter = new BufferedWriter(new FileWriter(Main.EXERCISE_PATH));
BufferedWriter answerWriter = new BufferedWriter(new FileWriter(Main.ANSWER_PATH));
//清空文件
exerciseWriter.flush();
answerWriter.flush();
//分别写入题目与答案于两个文件中
int currentQuestion = 1;
for (Question question : questionsList) {
String answer = question.getAnswer();
exerciseWriter.write("第" + currentQuestion + "题:" + question.toString() + " =");
exerciseWriter.newLine();
answerWriter.write(currentQuestion + ". " + answer);
answerWriter.newLine();
currentQuestion++;
}
//结束,关闭流
exerciseWriter.close();
answerWriter.close();
} /**
* 随机生成题目与对应的答案, 存入 questionsList
*/
public static void getQuestion(int questionsSum, int range) {
//循环随机生成题目
while (questionsList.size() < questionsSum) {
List questionList = new LinkedList();
//该表达式是否包含括号
boolean havebrackets = false;
Random random = new Random();
//随机生成该题目的运算符个数
int operatorSum = random.nextInt(3) + 1;
int currentOperatorNum = 0;
while (currentOperatorNum < operatorSum) {
List list = new LinkedList();
//生成运算符
String operator = Util.getOperator();
//决定是否为分数
if (random.nextBoolean()) {
//生成分数
Fraction fraction = Util.getFraction(range);
list.add(fraction);
if (havebrackets) {
list.add(")");
}
list.add(operator);
} else {
//生成整数
int num = random.nextInt(range) + 1;
list.add(new Fraction(num, 1));
if (havebrackets) {
list.add(")");
}
list.add(operator);
}
currentOperatorNum++;
//处理括号
if (havebrackets) {
havebrackets = false;
questionList.addAll(list);
continue;
}
//如果是乘除法,则不考虑括号
if ("×".equals(operator) || "÷".equals(operator) || operatorSum == 1) {
questionList.addAll(list);
continue;
}
//如果是加减法,决定是否存在括号
if (random.nextBoolean()) {
list.add(0, "(");
havebrackets = true;
}
questionList.addAll(list);
}
if (random.nextBoolean()) {
Fraction fraction = Util.getFraction(range);
questionList.add(fraction);
} else {
int num = random.nextInt(range) + 1;
questionList.add(new Fraction(num, 1));
}
if (havebrackets) {
questionList.add(")");
}
//生成题目答案
Fraction answer = getAnswer(new LinkedList(questionList));
if (isNegative) {
isNegative = false;
answerList.clear();
operatorList.clear();
continue;
}
Question question = new Question(new ArrayList<>(answerList),
new ArrayList<>(operatorList), questionList, Util.getNum(answer).toString());
boolean isRepeat = false;
//检查是否重复
if (questionsList.parallelStream().anyMatch(qf -> qf.equals(question))) {
answerList.clear();
operatorList.clear();
isRepeat = true;
}
if (!isRepeat) {
questionsList.add(question);
}
}
} /**
* 生成题目答案
*
* @param questionList 题目
*/
static Fraction getAnswer(List questionList) {
//优先处理括号里的表达式
while (questionList.contains("(")) {
int h1 = questionList.indexOf("(");
int h2 = questionList.indexOf(")");
//获得括号内的表达式
List havebracketsList = questionList.subList(h1 + 1, h2);
//安置计算出来的答案
questionList.add(h2 + 1, getAnswer(havebracketsList));
//移除原表达式,以进行下一步计算
for (int i = 0; i < 5; i++) {
questionList.remove(h1);
}
}
//先计算乘除法
while ((questionList.contains("×") || questionList.contains("÷")) && !isNegative) {
int multiply = questionList.indexOf("×");
int divide = questionList.indexOf("÷");
if (multiply < divide && multiply != -1 || divide == -1) {
//计算乘法
questionList = calculate(questionList, multiply, Fraction::multiply);
operatorList.add("×");
} else {
//计算除法
questionList = calculate(questionList, divide, Fraction::divide);
operatorList.add("÷");
}
}
//后计算加减法
while ((questionList.contains("+") || questionList.contains("-")) && !isNegative) {
int add = questionList.indexOf("+");
int subtract = questionList.indexOf("-");
if (add < subtract && add != -1 || subtract == -1) {
//计算加法
questionList = calculate(questionList, add, Fraction::add);
operatorList.add("+");
} else {
//计算减法
questionList = calculate(questionList, subtract, Fraction::subtract);
operatorList.add("-");
}
}
//返回答案
return (Fraction) questionList.get(0);
} /**
* 计算题目其中一个表达式
*
* @param list 题目中的数字运算符序列
* @param index 表达式中运算符所在位置
* @param fractionArithmetic 四则运算计算函数接口
*/
private static List calculate(List list, int index, FractionArithmetic fractionArithmetic) {
//计算数值1
Fraction fraction1 = (Fraction) list.get(index - 1);
//计算数值2
Fraction fraction2 = (Fraction) list.get(index + 1);
//计算结果
Fraction fraction = fractionArithmetic.arithmetic(fraction1, fraction2);
//如果计算的结果是负数
if (fraction.getNumerator() <= 0) {
isNegative = true;
}
answerList.add(fraction);
if (list.size() == 3) {
return new LinkedList(Collections.singletonList(fraction));
} else {
list.remove(index);
list.remove(index);
list.remove(index - 1);
list.add(index - 1, fraction);
return list;
}
}
}

CheckService 核查答案

@SuppressWarnings("unchecked")
public class CheckSevice {
//正确题目个数
private static int correct = 0;
//错误题目个数
private static int wrong = 0;
//正确题目列表
private static List<Integer> correctList = new ArrayList<>();
//错误题目列表
private static List<Integer> wrongList = new ArrayList<>(); /**
* 比对题目与答案的正确与否
*
* @param exercisePath 题目文件路径
* @param answerPath 答案文件路径
*/
public static void checkExercise(String exercisePath, String answerPath) throws IOException {
BufferedReader exerciseReader = new BufferedReader(new FileReader(exercisePath));
List<String> operatorList = new ArrayList<>(Arrays.asList("+", "-", "×", "÷", "(", ")"));
//读取题目文件,并生成题目的答案
List<Fraction> realAnswerList = exerciseReader.lines()
.map(s -> s.substring(s.indexOf(":") + 1, s.indexOf("=")).trim())
.map(s -> s.split("\\s+"))
.map(array -> {
List questionList = new ArrayList();
for (String s : array) {
//转换为分数
if (!operatorList.contains(s)) {
questionList.add(Util.getFraction(s));
} else {
questionList.add(s);
}
}
return questionList;
})
.map(ExerciseSevice::getAnswer)
.collect(toList());
//读取答案文件
BufferedReader answerReader = new BufferedReader(new FileReader(answerPath));
List<Fraction> answerList = answerReader.lines()
.map(s -> s.substring(s.indexOf(".") + 1).trim())
.map(Util::getFraction)
.collect(toList());
//比对答案与正确答案,生成结果
getCheckResult(realAnswerList, answerList);
//将结果写入文件中
BufferedWriter gradeWriter = new BufferedWriter(new FileWriter(Main.GRADE_PATH));
gradeWriter.flush();
gradeWriter.write("Correct: " + correct + " " + correctList);
gradeWriter.newLine();
gradeWriter.write("Wrong: " + wrong + " " + wrongList);
//结束,关闭流
gradeWriter.close();
} /**
* 比对答案
*
* @param realAnswerList 正确答案
* @param answerList 输入的答案
*/
private static void getCheckResult(List<Fraction> realAnswerList, List<Fraction> answerList) {
for (int i = 0; i < realAnswerList.size(); i++) {
//题目正确
if (realAnswerList.get(i).equals(answerList.get(i))) {
correct++;
correctList.add(i + 1);
} else {
//题目错误
wrong++;
wrongList.add(i + 1);
}
}
}
}

五. 测试运行

public class ExerciseTest {
static Map<Integer, Integer> map = new HashMap<>();
static {
map.put(10000, 10);
map.put(1000, 5);
map.put(10, 5);
map.put(20, 10);
map.put(30, 20);
map.put(50, 10);
map.put(100, 10);
map.put(200, 10);
map.put(300, 3);
map.put(120, 5);
} @Test
public void main() throws Exception {
for (int questionSum : map.keySet()) {
ExerciseSevice.getQuestion(questionSum, map.get(questionSum));
int currentQuestion = 1;
for (Question question : ExerciseSevice.questionsList) {
String answer = question.getAnswer();
System.out.println("第" + currentQuestion + "题:" + question.toString() + " = " + answer);
currentQuestion++;
}
} }
} public class CheckTest {
private final static String PROJECT_PATH = new File("").getAbsolutePath();
private final static String EXERCISE_PATH = PROJECT_PATH + "\\src\\main\\resources\\exercise.txt";
private final static String ANSWER_PATH = PROJECT_PATH + "\\src\\main\\resources\\answers.txt"; @Test
public void main() throws Exception {
CheckSevice.checkExercise(EXERCISE_PATH, ANSWER_PATH);
}
}

六. 总结

本次结对编程由两个人共同合作,两个人在项目的进行中进行了很多交流,统一了主要思路,但是在实际开发中仍然遇到了不少问题,也一起进行了很多的思考。这次的编程,懂得了如何去合作,去交流,大大提高了开发效率。

四则运算 Java (于泽浩,袁浩越)的更多相关文章

  1. 四则运算 Java 杨辉鹏,郑冠华

    四则运算 Java 杨辉鹏,郑冠华 GitHub链接:https://github.com/yanghuipeng/arithmetic 项目相关要求 使用 -n 参数控制生成题目的个数,例如 -n ...

  2. 小学生四则运算JAVA

    点我,github地址 组员:黄浩格,何坤 一.项目说明 1题目:实现一个自动生成小学四则运算题目的命令行程序. 2说明: 自然数:0, 1, 2, -. • 真分数:1/2, 1/3, 2/3, 1 ...

  3. 四则运算Java语言实验设计过程1

    题目要求: 像二柱子那样,花二十分钟写一个能自动生成三十道小学四则运算题目的 “软件”.要求:除了整数以外,还要支持真分数的四则运算(需要验证结果的正确性).题目避免重复.可定制出题的数量. 设计思路 ...

  4. 四则运算(Java)--温铭淇,付夏阳

    GitHub项目地址: https://github.com/fxyJAVA/Calculation 四则运算项目要求: 程序处理用户需求的模式为: Myapp.exe -n num -r size ...

  5. 四则运算 Java 实现 刘丰璨,王翠鸾

    四则运算 GitHub仓库 功能实现 [x] 使用 -n 参数控制生成题目的个数,并且根据解空间限制用户设定的范围(如 range == 2 时,用户却要求生成 10000 道题目,这明显不合理) [ ...

  6. 结对编程四则运算--JAVA实现(徐静、林文敏)

    Github项目地址 项目相关要求 -n 参数控制生成题目的个数 (√) Myapp.exe -n 10 // 将生成10个题目 -r 参数控制题目中数值(自然数.真分数和真分数分母)的范围 (√) ...

  7. 四则运算 Java 姚康友,黎扬乐

    github项目传送门:https://github.com/yaokangyou/arithmetic 项目要求 功能列表 [完成] 使用 -n 参数控制生成题目的个数 [完成] 使用 -r 参数控 ...

  8. 算法笔记_156:算法提高 6-17复数四则运算(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 设计复数库,实现基本的复数加减乘除运算. 输入时只需分别键入实部和虚部,以空格分割,两个复数之间用运算符分隔:输出时按a+bi的格式在屏幕上打印结果 ...

  9. 四则运算(Java) 陈志海 邓宇

    目录 Github项目地址 PSP表格 功能要求 题目 功能(已全部实现) 效能分析 设计实现过程 数值生成 算式生成 问题集生成 设计实现过程 代码说明 测试运行 代码覆盖率 项目小结 Github ...

随机推荐

  1. spring中的BeanFactory和FactoryBean的区别与联系

    首先,这俩都是个接口… 实现 BeanFactory 接口的类表明此类是一个工厂,作用就是配置.新建.管理 各种Bean. 而 实现 FactoryBean 的类表明此类也是一个Bean,类型为工厂B ...

  2. DOM 练习

    练习一: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3 ...

  3. JDK中rt.jar、tools.jar和dt.jar作用

    dt.jar和tools.jar位于:{Java_Home}/lib/下,而rt.jar位于:{Java_Home}/jre/lib/下,其中: rt.jar是JAVA基础类库,也就是你在java d ...

  4. 关于“Durian”调查问卷的心得体会

    这周我们做了项目着手前的客户需求调查,主要以调查问卷的方式进行.其实做问卷调查并不是想象中的那么简单,首先要确定问卷调查的内容,每一个问题都要经过深思熟虑,字字斟酌,既要切合问卷主要目的,又要简洁扼要 ...

  5. 72. Edit Distance (String; DP)

    Given two words word1 and word2, find the minimum number of steps required to convert word1 to word2 ...

  6. Spring配置连接池

    ---------------------siwuxie095                                 Spring 配置连接池         1.Spring 配置内置连接 ...

  7. Asp.net中判断是否是指定页面请求的代码示例

    //获取请求网址,非法请求,返回主页 if (Request.UrlReferrer != null) { string requstUrl = Request.UrlReferrer.Absolut ...

  8. 安装Python3后,centos使用yum报错

    题记 在之前的文章中我自定义安装了Python3,并且修改了默认的 Python软链,今天想搭建一个 ftp 服务器,使用命令的时候出现了一个错误: 问题 1.使用 yum 安装 ftp工具 yum ...

  9. java里的静态成员变量是放在了堆内存还是栈内存

    转自http://bbs.csdn.NET/topics/370001490 堆区: 1.存储的全部是对象,每个对象都包含一个与之对应的class的信息.(class的目的是得到操作指令) 2.jvm ...

  10. 我的UI启蒙之路

    为什么叫UI启蒙之路呢? 我没有学过美术,也不懂设计,但是有的时候也许就是一种命中注定吧,让我知道了UI,并且一发不可收拾的爱上了它. 具体情况是这样的: 我毕业于电力学校,是一名不折不扣的工科生,专 ...