小学四则运算生成Java实现 (彭迪彬 李尤)
Posted 2youyou
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了小学四则运算生成Java实现 (彭迪彬 李尤)相关的知识,希望对你有一定的参考价值。
Java实现小学四则运算
by 彭迪彬 李尤
GitHub项目地址:https://github.com/2youyou/JDBC
项目要求
题目:实现一个自动生成小学四则运算题目的命令行程序
- 功能列表
- [完成] 使用 -n 参数控制生成题目的个数。
- [完成] 使用 -r 参数控制题目中数值的范围。
- [完成] 生成的题目中计算过程不能产生负数。
- [完成] 生成的题目中如果存在形如e1 ÷ e2的子表达式,那么其结果应是真分数。
- [完成] 程序一次运行生成的题目不能重复,生成的题目存入执行程序的当前目录下的Exercises.txt文件。
- [完成] 每道题目中出现的运算符个数不超过3个。
- [完成] 在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件。
- [完成] 程序应能支持一万道题目的生成。
- [完成] 程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计。
设计实现
1.表达式生成:根据输入的两个参数决定表达式的数量及数值范围,随机生成数值范围内的自然数及运算符,随机插入左括号并在相应的位置插入右括号。
2.分数:专门写了一个类Fraction来生成分数,并且有约分的功能。
3.负数:负数只会在减法运算时产生,每当遇到减法运算时,若运算结果出现负数,就重新生成新的表达式。
4.计算:将整数也当成分数进行计算,先将上面生成的表达式(以字符串存储)分别入数字栈和符号栈,再根据符号优先级计算最终结果。
代码
分数类
public class Fraction {
int x; //分子
int y; //分母
private Fraction temp;
public Fraction(int a, int b) {
x = a;
y = b;
}
Fraction add(Fraction r) {
temp = new Fraction(0, 0);
temp.x = x * r.y + y * r.x;
temp.y = y * r.y;
return temp;
}
Fraction minus(Fraction r) {
temp = new Fraction(0, 0);
temp.x = x * r.y - y * r.x;
temp.y = y * r.y;
return temp;
}
Fraction multiply(Fraction r) {
temp = new Fraction(0, 0);
temp.x = x * r.x;
temp.y = y * r.y;
return temp;
}
Fraction divide(Fraction r) {
temp = new Fraction(0, 0);
temp.x = x * r.y;
temp.y = y * r.x;
return temp;
}
String print() {
/**
* 计算结果化简
*/
if (x == 0) {
return "0";
} else {
int n;
if (x > y)
n = x;
else
n = y;
int maxn = 0;
for (int i = 1; i <= n; ++i) { //约分
if (x % i == 0 && y % i == 0)
maxn = i;
}
int a = x / maxn;
int b = y / maxn;
if (a == b)
return "1";
else if(b==1)
return a+"";
else
return a + "/" + b;
}
}
}
表达式生成
public class CreateExercise {
String[] sign = {"+", "-", "x", "÷", "/"};
int range_num;
Random random = new Random();
public void setRange_num(int range_num) {
this.range_num =range_num;
}
public String create() {
String str = "";
int local = random.nextInt(3);
for (int j = 0; j < 3; j++) {
if (local == 0 && j == 0) {
str += "(";
} else if (local == 2 && j == 1) {
str += "(";
}
str += random.nextInt(range_num) % range_num + 1; //产生指定范围随机数
if (local == 0 && j == 1) {
str += ")";
}
if (local == 2 && j == 2) {
str += ")";
}
String signElement = sign[random.nextInt(5)];//产生随机运算符号
str += signElement;
if (signElement == "/") {
str += random.nextInt(range_num) % range_num + 1;
signElement = sign[random.nextInt(5)];
while (true) {
if (signElement != "/") {
str += signElement;
break;
}
signElement = sign[random.nextInt(5)];
}
}
}
str = str.substring(0, str.length() - 1);
return str;
}
public void belongFraction(String strfraction) {
/**
* 处理分数计算
*/
String[] fractionlist = null;
if (strfraction.contains("+")) {
fractionlist = strfraction.split("\\+");
CalculateFraction(fractionlist, 0);
} else if (strfraction.contains("-")) {
fractionlist = strfraction.split("-");
CalculateFraction(fractionlist, 1);
} else if (strfraction.contains("x")) {
fractionlist = strfraction.split("\\x");
CalculateFraction(fractionlist, 2);
} else if (strfraction.contains("÷")) {
fractionlist = strfraction.split("÷");
CalculateFraction(fractionlist, 3);
}
}
public void CalculateFraction(String[] strlist, int flag) {
/**
* 分数的四种基本运算
*/
String[] fraction1 = new String[2];
String[] fraction2 = new String[2];
if (strlist[0].contains("/"))
fraction1 = strlist[0].split("/");
else {
fraction1[0] = strlist[0];
fraction1[1] = "1";
}
if (strlist[1].contains("/"))
fraction2 = strlist[1].split("/");
else {
fraction2[0] = strlist[1];
fraction2[1] = "1";
}
Fraction fr1 = new Fraction(Integer.parseInt(fraction1[0]), Integer.parseInt(fraction1[1]));
Fraction fr2 = new Fraction(Integer.parseInt(fraction2[0]), Integer.parseInt(fraction2[1]));
fr1.print();
switch (flag) {
case 0:
fr1.add(fr2).print();
break;
case 1:
fr1.minus(fr2).print();
break;
case 2:
fr1.multiply(fr2).print();
break;
case 3:
fr1.divide(fr2).print();
break;
}
}
}
计算
public class Calculator {
/**
* 数字栈:存储表达式中的数字
*/
private Stack<String> numStack = null;
/**
* 符号栈:存储表达式中的运算符和括号
*/
private Stack<Character> charStack = null;
/**
* 计算四则运算表达式,返回计算结果
*
*/
public String calculate(String numStr) {
numStr = removeStrSpace(numStr);
if (numStr.length() > 1 && !"=".equals(numStr.charAt(numStr.length() - 1) + "")) {
numStr += "=";
}
if (!isStandard(numStr)) {
return "0";
}
numStack = new Stack<String>();
charStack = new Stack<Character>();
StringBuffer temp = new StringBuffer();
for (int i = 0; i < numStr.length(); i++) {
char ch = numStr.charAt(i);
if (isNumber(ch) || ch == ‘/‘) {
temp.append(ch);
} else {
String tempStr = temp.toString();
if (!tempStr.isEmpty()) {
numStack.push(tempStr);
temp = new StringBuffer();
}
while (!comparePri(ch) && !charStack.empty()) {
String a = numStack.pop();
String b = numStack.pop();
Fraction f1 = null;
Fraction f2 = null;
if (a.contains("/")) {
String[] alist = a.split("/");
f1 = new Fraction(Integer.parseInt(alist[0]), Integer.parseInt(alist[1]));
} else {
f1 = new Fraction(Integer.parseInt(a), 1);
}
if (b.contains("/")) {
String[] blist = b.split("/");
f2 = new Fraction(Integer.parseInt(blist[0]), Integer.parseInt(blist[1]));
} else {
f2 = new Fraction(Integer.parseInt(b), 1);
}
switch (charStack.pop()) {
case ‘+‘:
numStack.push(f2.add(f1).print());
break;
case ‘-‘:
if ((f1.x/f1.y) >= (f2.x/f2.y)) {
return null;
}
numStack.push(f2.minus(f1).print());
break;
case ‘x‘:
numStack.push(f2.multiply(f1).print());
break;
case ‘÷‘:
if (f1.x==0){
return null;
}
numStack.push(f2.divide(f1).print());
break;
default:
break;
}
}
if (ch != ‘=‘) {
charStack.push(new Character(ch));
if (ch == ‘)‘) {
charStack.pop();
charStack.pop();
}
}
}
}
return numStack.pop();
}
private String removeStrSpace(String str) {
return str != null ? str.replaceAll(" ", "") : "";
}
private boolean isStandard(String numStr) {
if (numStr == null || numStr.isEmpty())
return false;
Stack<Character> stack = new Stack<Character>();
boolean b = false;
for (int i = 0; i < numStr.length(); i++) {
char n = numStr.charAt(i);
if (!(isNumber(n) || "(".equals(n + "") || ")".equals(n + "")
|| "+".equals(n + "") || "-".equals(n + "")
|| "x".equals(n + "") || "÷".equals(n + "") || "/".equals(n + "")
|| "=".equals(n + ""))) {
return false;
}
if ("(".equals(n + "")) {
stack.push(n);
}
if (")".equals(n + "")) {
if (stack.isEmpty() || !"(".equals((char) stack.pop() + ""))
return false;
}
if ("=".equals(n + "")) {
if (b)
return false;
b = true;
}
}
if (!stack.isEmpty())
return false;
if (!("=".equals(numStr.charAt(numStr.length() - 1) + "")))
return false;
return true;
}
private boolean isNumber(char num) {
if (num >= ‘0‘ && num <= ‘9‘)
return true;
return false;
}
/**
* 比较优先级:如果当前运算符比栈顶元素运算符优先级高则返回true,否则返回false
*/
private boolean comparePri(char symbol) {
if (charStack.empty()) {
return true;
}
char top = charStack.peek();
if (top == ‘(‘) {
return true;
}
switch (symbol) {
case ‘(‘:
return true;
case ‘x‘: {
if (top == ‘+‘ || top == ‘-‘)
return true;
else
return false;
}
case ‘÷‘: {
if (top == ‘+‘ || top == ‘-‘)
return true;
else
return false;
}
case ‘+‘:
return false;
case ‘-‘:
return false;
case ‘)‘:
return false;
case ‘=‘:
return false;
default:
break;
}
return true;
}
String getFinalResult(String str) {
if (!str.contains("/"))
return str;
String[] part = str.split("/");
int a = Integer.parseInt(part[0]);
int b = Integer.parseInt(part[1]);
if (a == b)
return "1";
else if (a > b && a % b != 0) {
return a / b + "’" + a % b + "/" + b;
} else if (a < b && -a > b && (-a) % b != 0) {
return "-" + (-a) / b + "’" + (-a) % b + "/" + b;
} else if (b == 1)
return a + "";
else
return a + "/" + b;
}
}
生成txt文本
public class IO {
File ExerciseFile = null;
File AnswerFile = null;
String filename = "";
BufferedWriter ExerciseOut = null;
BufferedWriter AnswerOut = null;
public IO() {
if (this.CreateFile()) {
this.setOutBufferedWriter();
} else
System.out.println("创建文件失败!");
}
public void setOutBufferedWriter() {
try {
this.ExerciseOut = new BufferedWriter(new FileWriter(ExerciseFile));
this.AnswerOut=new BufferedWriter(new FileWriter(AnswerFile));
} catch (IOException e) {
e.printStackTrace();
}
}
public boolean CreateFile() {
String relativelyPath=System.getProperty("user.dir");
ExerciseFile = new File(relativelyPath+"\\Exercise" + ".txt");
AnswerFile = new File(relativelyPath+"\\Answer" + ".txt");
if (ExerciseFile.exists()) {
ExerciseFile.delete();
}
if (AnswerFile.exists()) {
AnswerFile.delete();
}
try {
ExerciseFile.createNewFile();
AnswerFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
return true;
}
public boolean WriteToFile(String content, int flag) {
try {
switch (flag) {
case 0:
ExerciseOut.write(content);
ExerciseOut.write("
");
ExerciseOut.flush();
return true;
case 1:
AnswerOut.write(content);
AnswerOut.write("
");
AnswerOut.flush();
return true;
}
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
public boolean CloseOutBufferedWriter() {
try {
ExerciseOut.close();
AnswerOut.close();
return true;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
}
主函数启动类
public class MyApp {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
System.out.print("请输入希望生成的题目数量:");
int problems_num = scan.nextInt();
System.out.print("请输入题目的数值范围n(范围为0到n):");
int range_num = scan.nextInt();
System.out.print("是否开始答题(yes/no):");
String is_doNow=scan.next();
LinkedHashMap<Integer, String> rightAnswerMap = new LinkedHashMap<Integer, String>();
LinkedHashMap<Integer,String> exerciseMap = new LinkedHashMap<Integer, String>();
ArrayList<Integer> rightRecord = new ArrayList<Integer>();
ArrayList<Integer> wrongRecord = new ArrayList<Integer>();
CreateExercise ce = new CreateExercise();
IO save = new IO();
ce.setRange_num(range_num);
for (int i = 1; i <= problems_num; i++) {
String problem = ce.create();
exerciseMap.put(i,problem);
String ns = problem;
int rightbrackets;
int leftbrackets;
if (problem.contains(")")) {
rightbrackets = problem.indexOf(")");
leftbrackets = problem.indexOf("(");
if (rightbrackets != problem.length() - 1 && problem.charAt(rightbrackets + 1) == ‘/‘) {
StringBuilder sb = new StringBuilder(problem);
if (leftbrackets - 1 > 0 && problem.charAt(leftbrackets - 1) == ‘÷‘)
sb.replace(rightbrackets + 1, rightbrackets + 2, "x");
else
sb.replace(rightbrackets + 1, rightbrackets + 2, "÷");
ns = sb.toString();
}
}
Calculator cal = new Calculator();
String result = cal.calculate(ns);
if (result != null) {
result = cal.getFinalResult(result);
System.out.println(i + ": " + problem + "=" );
rightAnswerMap.put(i, result);
if (!save.WriteToFile(i + ": " + problem + "=", 0)) {
System.out.println("生成Exercise文件失败!");
System.exit(0);
}
if (!save.WriteToFile(+ i + ": " + problem + "=" + result, 1)) {
System.out.println("生成Answer文件失败!");
System.exit(0);
}
} else {
i--;
}
}
if(is_doNow.equals("yes")){
System.out.println("请输入答案(带分数用“ ’ ”分隔,如1’1/2):");
for (int i = 1; i <= problems_num; i++) {
System.out.print( + i + ": " + exerciseMap.get(i) + "=");
String input = scan.next();
if (rightAnswerMap.get(i).equals(input))
rightRecord.add(i);
else
wrongRecord.add(i);
}
System.out.println("结果为:");
if (rightRecord.size()!=0){
System.out.print("正确题目:"+rightRecord.size()+" (");
for (int i=0;i<rightRecord.size()-1;i++)
System.out.print(rightRecord.get(i)+",");
System.out.println(rightRecord.get(rightRecord.size()-1)+")");
}
else
System.out.println("正确题目:"+rightRecord.size());
if (wrongRecord.size()!=0){
System.out.print("错误题目:"+wrongRecord.size()+" (");
for (int i=0;i<wrongRecord.size()-1;i++)
System.out.print(wrongRecord.get(i)+",");
System.out.println(wrongRecord.get(wrongRecord.size()-1)+")");
}
else
System.out.println("错误题目:"+wrongRecord.size());
}
if (save.CloseOutBufferedWriter())
System.out.println("题目和答案文本创建成功");
else
System.out.println("题目和答案文本创建失败");
}
}
测试运行
生成10000道题目
部分题目及答案
答案统计
故意答错 1,3题
代码覆盖率
PSP
PSP2.1
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 60 | 120 |
· Estimate | · 估计这个任务需要多少时间 | 30 | 60 |
Development | 开发 | 1000 | 2200 |
· Analysis | · 需求分析 (包括学习新技术) | 100 | 180 |
· Design Spec | · 生成设计文档 | 60 | 100 |
· Design Review | · 设计复审 (和同事审核设计文档) | 30 | 30 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 30 | 30 |
· Design | · 具体设计 | 120 | 150 |
· Coding | · 具体编码 | 600 | 1500 |
· Code Review | · 代码复审 | 50 | 60 |
· Test | · 测试(自我测试,修改代码,提交修改) | 150 | 60 |
Reporting | 报告 | 80 | 80 |
· Test Report | · 测试报告 | 30 | 40 |
· Size Measurement | · 计算工作量 | 20 | 10 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 40 | 20 |
合计 | 1150 | 2400 |
总结
此次的结对编程,我主要负责表达式的生成、计算,李尤同学主要负责文件生成的IO,我们都相互审阅了对方的代码。刚拿到题目的时候,我们的思路并不是很清晰,对于表达式的计算及负数的避免想要直接实现,尝试过后发现过于麻烦,后来与李尤同学讨论后发现使用数据结构的栈来实现会简单很多,可以说这次的结对编程,通过两个人的讨论,使得项目的整体思路变得很清晰,实现起来也更加容易。此次编程较为遗憾的没有实现题目的查重功能,我们以后会继续努力完善这个功能。李尤同学的思维很活跃,经常有一些天马行空的想法,在编写程序的过程中给了我很多启发。这次的结对编程,让我体会到了合作的力量,同时也认识到了自己数据结构方面知识的不足,今后会多加学习这方面的知识,完善自己的不足。
以上是关于小学四则运算生成Java实现 (彭迪彬 李尤)的主要内容,如果未能解决你的问题,请参考以下文章