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. post与get的区别

    GET请求在URL中传送的参数大多数浏览器限制该长度为2kb的,而POST没有. GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息. GET参数通过URL传递,POST放 ...

  2. xpath定位--绝对与相对的定位

    xpath定位--绝对与相对的定位: xpath定位即为xml路径语言,它是一种用来确定xml文档中某部分位置的语言,xpath基于xml的树状结构,提供在数据结构中找寻节点的能力 xpath的相对定 ...

  3. windows server 2008 远程桌面连接数修改--无限连接

    1.开启远程桌面 我的电脑 |  属性 |  远程设置  |  远程 |  进允许运行使用网络级别身份验证的远程桌面的计算机连接(更安全)(N)

  4. 对象池 object pool

    对象池适用于: 对象的创建很耗时 对象频繁地释放再创建 对象池的实现:将释放的对象放进对象池中,在新建对象时,从对象池取对象 public class ObjectPool<T> wher ...

  5. 在UNITY中按钮的高亮用POINT灯实现,效果别具一番风味

    在UNITY中按钮的高亮用POINT灯实现,效果别具一番风味

  6. CloseableHttpClient(二)

    package com.cmy.httpClient; import java.io.IOException; import org.apache.http.HttpEntity; import or ...

  7. 转)安装svn服务器

    以下转载自:http://www.linuxidc.com/Linux/2015-01/111956.htm 安装 安装软件包: sudo apt-get install subversion 配置 ...

  8. [leetcode]125. Valid Palindrome判断回文串

    Given a string, determine if it is a palindrome, considering only alphanumeric characters and ignori ...

  9. 图像获取与采集及图像格式与Region介绍——第2讲

    一.图像获取与采集 1.本地图片读取 ① 单张读取 直接传入图片路径即可,可以用绝对路径,也可以用相对路径: read_image (Image, 'C:/Users/Administrator/De ...

  10. Golang之定时器,recover

    滴答滴答……定时器的使用 package main import ( "fmt" "time" ) //定时器的使用 func main() { t := ti ...