四则运算 Java (于泽浩,袁浩越)
一. 项目要求
题目 实现一个自动生成小学四则运算题目的命令行程序。
需求(全部完成)
- 使用 -n 参数控制生成题目的个数
Myapp.exe -n 10
- 使用 -r 参数控制题目中数值(自然数、真分数和真分数分母)的范围
Myapp.exe -r 10
- 生成的题目中计算过程不能产生负数,也就是说算术表达式中如果存在形如e1 − e2的子表达式,那么e1 ≥ e2。
- 生成的题目中如果存在形如e1 ÷ e2的子表达式,那么其结果应是真分数。
- 每道题目中出现的运算符个数不超过3个。
- 程序一次运行生成的题目不能重复,即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目。即不能通过有限次变换变成同一道题目,下面例子均为重复题目。
- 23+45 与 45+23
- 3+(2+1)与1+2+3
生成的题目存入执行程序的当前目录下的Exercises.txt文件,格式如下:
- 四则运算题目 1
- 四则运算题目 2
……
真分数五分之三表示为3/5,真分数二又八分之三表示为2’3/8
- 在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件,格式如下
- 答案1
- 答案2
特别的,真分数的运算如下例所示
1/6 + 1/8 = 7/24
- 程序应能支持一万道题目的生成。
- 程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计,结果输出至 Grade.txt
Myapp.exe -e <exercisefile>.txt -a <answerfile>.txt
格式如下
Correct: 5 (1, 3, 5, 7, 9)
二. PSP
三. 思路分析
生成题目:
使用随机函数随机决定运算符个数,运算符类型,数值是否为分数(真分数或假分数),是否包含括号等因素;
在生成数值时,统一先使用 apache 类库中的 Fraction 分数类来封装,以方面后面的计算;
每循环生成好一道题目,便调用方法生成答案,生成答案时利用递归的方式一步步算出最终答案,并将题目与答案一同封装进 Question 类中,生成答案时遇到负数,选择跳过重新生成题目;
检查重复题目,会根据题目的运算符的优先计算进行重新排序,并将同一个运算符的两个数值根据大小的关系进行了排序,再进行查重。
核对答案:
这个功能需要读取两个文件,读取题目文件时,计算出题目的正确答案,最后读取答案文件,将两者对比即可。
四. 代码说明
主要的类
- Main 启动类
- ExerciseService 生成题目与答案 (-n -r 命令)
- 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 (于泽浩,袁浩越)的更多相关文章
- 四则运算 Java 杨辉鹏,郑冠华
四则运算 Java 杨辉鹏,郑冠华 GitHub链接:https://github.com/yanghuipeng/arithmetic 项目相关要求 使用 -n 参数控制生成题目的个数,例如 -n ...
- 小学生四则运算JAVA
点我,github地址 组员:黄浩格,何坤 一.项目说明 1题目:实现一个自动生成小学四则运算题目的命令行程序. 2说明: 自然数:0, 1, 2, -. • 真分数:1/2, 1/3, 2/3, 1 ...
- 四则运算Java语言实验设计过程1
题目要求: 像二柱子那样,花二十分钟写一个能自动生成三十道小学四则运算题目的 “软件”.要求:除了整数以外,还要支持真分数的四则运算(需要验证结果的正确性).题目避免重复.可定制出题的数量. 设计思路 ...
- 四则运算(Java)--温铭淇,付夏阳
GitHub项目地址: https://github.com/fxyJAVA/Calculation 四则运算项目要求: 程序处理用户需求的模式为: Myapp.exe -n num -r size ...
- 四则运算 Java 实现 刘丰璨,王翠鸾
四则运算 GitHub仓库 功能实现 [x] 使用 -n 参数控制生成题目的个数,并且根据解空间限制用户设定的范围(如 range == 2 时,用户却要求生成 10000 道题目,这明显不合理) [ ...
- 结对编程四则运算--JAVA实现(徐静、林文敏)
Github项目地址 项目相关要求 -n 参数控制生成题目的个数 (√) Myapp.exe -n 10 // 将生成10个题目 -r 参数控制题目中数值(自然数.真分数和真分数分母)的范围 (√) ...
- 四则运算 Java 姚康友,黎扬乐
github项目传送门:https://github.com/yaokangyou/arithmetic 项目要求 功能列表 [完成] 使用 -n 参数控制生成题目的个数 [完成] 使用 -r 参数控 ...
- 算法笔记_156:算法提高 6-17复数四则运算(Java)
目录 1 问题描述 2 解决方案 1 问题描述 设计复数库,实现基本的复数加减乘除运算. 输入时只需分别键入实部和虚部,以空格分割,两个复数之间用运算符分隔:输出时按a+bi的格式在屏幕上打印结果 ...
- 四则运算(Java) 陈志海 邓宇
目录 Github项目地址 PSP表格 功能要求 题目 功能(已全部实现) 效能分析 设计实现过程 数值生成 算式生成 问题集生成 设计实现过程 代码说明 测试运行 代码覆盖率 项目小结 Github ...
随机推荐
- post与get的区别
GET请求在URL中传送的参数大多数浏览器限制该长度为2kb的,而POST没有. GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息. GET参数通过URL传递,POST放 ...
- xpath定位--绝对与相对的定位
xpath定位--绝对与相对的定位: xpath定位即为xml路径语言,它是一种用来确定xml文档中某部分位置的语言,xpath基于xml的树状结构,提供在数据结构中找寻节点的能力 xpath的相对定 ...
- windows server 2008 远程桌面连接数修改--无限连接
1.开启远程桌面 我的电脑 | 属性 | 远程设置 | 远程 | 进允许运行使用网络级别身份验证的远程桌面的计算机连接(更安全)(N)
- 对象池 object pool
对象池适用于: 对象的创建很耗时 对象频繁地释放再创建 对象池的实现:将释放的对象放进对象池中,在新建对象时,从对象池取对象 public class ObjectPool<T> wher ...
- 在UNITY中按钮的高亮用POINT灯实现,效果别具一番风味
在UNITY中按钮的高亮用POINT灯实现,效果别具一番风味
- CloseableHttpClient(二)
package com.cmy.httpClient; import java.io.IOException; import org.apache.http.HttpEntity; import or ...
- 转)安装svn服务器
以下转载自:http://www.linuxidc.com/Linux/2015-01/111956.htm 安装 安装软件包: sudo apt-get install subversion 配置 ...
- [leetcode]125. Valid Palindrome判断回文串
Given a string, determine if it is a palindrome, considering only alphanumeric characters and ignori ...
- 图像获取与采集及图像格式与Region介绍——第2讲
一.图像获取与采集 1.本地图片读取 ① 单张读取 直接传入图片路径即可,可以用绝对路径,也可以用相对路径: read_image (Image, 'C:/Users/Administrator/De ...
- Golang之定时器,recover
滴答滴答……定时器的使用 package main import ( "fmt" "time" ) //定时器的使用 func main() { t := ti ...