结对项目-ExpressionBuilder
Posted zhengzichun
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了结对项目-ExpressionBuilder相关的知识,希望对你有一定的参考价值。
ExpressionBuilder
合作者:张强 & 郑梓淳
项目要求
实现一个自动生成小学四则运算题目的命令行程序(也可以用图像界面,具有相似功能)。
操作数:
- 自然数:0, 1, 2, …。
- 真分数:1/2, 1/3, 2/3, 1/4, 1’1/2, …。
符号:
- 运算符:+, ?, ×, ÷。
- 括号:(, )。
- 等号:=。
分隔符:
- 空格(用于四则运算符和等号前后)。
功能
- 生成表达式
- 显示答案
- 批改答案
要求
- 可以控制生成题目的数量
- 可以控制操作数数和答案的大小
- 题目不重复
- 整个运算过程不出现负数
- 运算符数量小于等于3
- 支持一万道题目的生成
设计实现过程
- Frame:实现GUI
- JTextFieldHintListener:文本框提示内容
- Builder:随机生成表达式
- Solve:计算表达式值,检查答案等
- Pair:分数计算类,实现了分数的加、减、乘、除、小于等
功能介绍
主界面
没有输入参数n或者r
生成n条题目,题目和答案范围为[0,r)
在右边框中作答,并检查答案(答案数量小于题目时则视为未作答,不记录错误)
前十道题手动计算并输入,后十道题输入-1,可以看出程序是正确的
在题目中显示答案
效能分析
复杂度
- 表达式生成是随机生成操作数和运算符进行组合,线性复杂度。
计算为转化为后缀表达式后计算,线性复杂度。
判重是使用在计算時候记录下来的各个中间数和符号的计算顺序,用字符串hash和hashset判重,也是线性复杂度。
实际
生成10k条表达式的时间为0.5-0.7s左右,
100k为4-5s左右,
1000k为47s-50s左右。
分析
从用时上看基本是符合线性复杂度的。
这么花时间的最要原因是随机生成的表达式合法率不高,
是因为表达式生成为随机的,最后还加了判断答案本身和答案的分母是否有超过限制r,不通过则直接舍弃,所以合法的表达式并不多。
如果仅判断答案是否小于0,则100k为1s,1000k为7-8s,少了80%的时间,合法表达式会多很多,基本没有冲突。
不过如果仅生成1w条,则输出时间会比表达式生成的时间多,所以0.5s感觉还可以接受。。不过有优化空间
部分代码说明
- Bulider类:用于随机产生表达式
//是否插入括号
int flag = r1.nextInt(2);
//随机生成1-3个运算符
int computeNum = r1.nextInt(3);
switch (computeNum) {
case 0:
expression.append(s[0] + " " + ch[0] + " " + s[1]);
break;
case 1:
if (flag == 1) {
//括号插入位置随机
int index = r1.nextInt(2);
if (index == 0) {
expression.append(
"(" + " " + s[0] + " " + ch[0] + " " + s[1] + " " + ")" + " " + ch[1] + " " + s[2]);
} else {
expression.append(
s[0] + " " + ch[0] + " " + "(" + " " + s[1] + " " + ch[1] + " " + s[2] + " " + ")");
}
} else {
expression.append(s[0] + " " + ch[0] + " " + s[1] + " " + ch[1] + " " + s[2]);
}
break;
case 2:
if (flag == 1) {
//括号插入位置随机
int index = r1.nextInt(6);
switch (index) {
case 0:
expression.append("(" + " " + s[0] + " " + ch[0] + " " + s[1] + " " + ")" + " " + ch[1]
+ " " + "(" + " " + s[2] + " " + ch[2] + " " + s[3] + " " + ")");
break;
case 1:
expression.append("(" + " " + s[0] + " " + ch[0] + " " + s[1] + " " + ")" + " " + ch[1]
+ " " + s[2] + " " + ch[2] + " " + s[3]);
break;
case 2:
expression.append("(" + " " + s[0] + " " + ch[0] + " " + s[1] + " " + ch[1] + " " + s[2]
+ " " + ")" + " " + ch[2] + " " + s[3]);
break;
case 3:
expression.append(s[0] + " " + ch[0] + " " + "(" + " " + s[1] + " " + ch[1] + " " + s[2]
+ " " + ")" + " " + ch[2] + " " + s[3]);
break;
case 4:
expression.append(s[0] + " " + ch[0] + " " + "(" + " " + s[1] + " " + ch[1] + " " + s[2]
+ " " + ch[2] + " " + s[3] + " " + ")");
break;
case 5:
expression.append(s[0] + " " + ch[0] + " " + s[1] + " " + ch[1] + " " + "(" + " " + s[2]
+ " " + ch[2] + " " + s[3] + " " + ")");
break;
default:
expression.append(" ");
}
} else {
expression.append(
s[0] + " " + ch[0] + " " + s[1] + " " + ch[1] + " " + s[2] + " " + ch[2] + " " + s[3]);
}
break;
default:
expression.append(" ");
}
- Frame类:GUI界面
//GUI中确定按钮监听
ok.addActionListener(e -> {
if (n.getText().equals("please enter the parameter n") || r.getText().equals("please enter the parameter r")) {
JOptionPane.showMessageDialog(null, "请输入参数!");
} else {
PrintWriter out_exp = null;
try {
out_exp = new PrintWriter("Exercise.txt");
} catch (FileNotFoundException ex) {
ex.printStackTrace();
}
PrintWriter out_ans = null;
try {
out_ans = new PrintWriter("Answers.txt");
} catch (FileNotFoundException ex) {
ex.printStackTrace();
}
Solve.Build(Integer.parseInt(r.getText()), Integer.parseInt(n.getText()), out_exp, out_ans);
out_exp.close();
out_ans.close();
out_exp.flush();
String filepath = System.getProperty("user.dir") + "/Exercise.txt";
File file = new File(filepath);
try {
BufferedReader input = new BufferedReader(new InputStreamReader(
new FileInputStream(file)));
jTextArea1.read(input, "READING FILE :-)");
} catch (Exception ee) {
ee.printStackTrace();
}
}
});
//GUI中检查按钮的监听
check.addActionListener(e1 -> {
PrintWriter Grade = null;
try {
Grade = new PrintWriter("Grade.txt");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
//将输入的答案写入inputAnswers文件中
StringTokenizer st = new StringTokenizer(jTextArea2.getText(), "
");
FileWriter inputAnswers = null;
try {
inputAnswers = new FileWriter("inputAnswers.txt");
} catch (IOException e) {
e.printStackTrace();
}
while (st.hasMoreTokens()) {
try {
inputAnswers.append(st.nextToken() + "
");
} catch (IOException e) {
e.printStackTrace();
}
}
try {
inputAnswers.close();
} catch (IOException e) {
e.printStackTrace();
}
Scanner in_exp = null;
try {
in_exp = new Scanner(Paths.get("Exercise.txt"));
} catch (IOException e) {
e.printStackTrace();
}
;
Scanner in_ans = null;
try {
in_ans = new Scanner(Paths.get("inputAnswers.txt"));
} catch (IOException e) {
e.printStackTrace();
}
Solve.check(in_exp, in_ans, Grade);
in_exp.close();
in_ans.close();
Grade.close();
//把Grade的内容以消息框显示出来
String filepath = System.getProperty("user.dir") + "/Grade.txt";
File file = new File(filepath);
String s;
StringBuilder str = new StringBuilder();
try {
BufferedReader br = new BufferedReader(new FileReader(file));
while ((s = br.readLine()) != null) {
str.append(s);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
JOptionPane.showMessageDialog(null, str.toString());
});
//GUI中查看答案按钮的监听
display.addActionListener(e -> {
//把题目和标准答案整合成一个文件
PrintWriter exerciseAndAnswer = null;
try {
exerciseAndAnswer = new PrintWriter("exerciseAndAnswer.txt");
} catch (FileNotFoundException ex) {
ex.printStackTrace();
}
String E;
String A;
String filepath = System.getProperty("user.dir") + "/Exercise.txt";
File file = new File(filepath);
String filepath1 = System.getProperty("user.dir") + "/Answers.txt";
File file1 = new File(filepath1);
try {
BufferedReader inputE = new BufferedReader(new FileReader(file));
BufferedReader inputA = new BufferedReader(new FileReader(file1));
while ((E = inputE.readLine()) != null && (A = inputA.readLine()) != null) {
StringBuilder s = new StringBuilder();
s.append(E + " " + A);
exerciseAndAnswer.println(s.toString());
}
} catch (Exception ee) {
ee.printStackTrace();
}
exerciseAndAnswer.close();
//读取
String filepath2 = System.getProperty("user.dir") + "/exerciseAndAnswer.txt";
File file2 = new File(filepath2);
try {
BufferedReader input = new BufferedReader(new InputStreamReader(
new FileInputStream(file2)));
jTextArea1.read(input, "READING FILE :-)");
} catch (Exception ee) {
ee.printStackTrace();
}
});
- Pair类的主要方法:分数运算
public class Pair {
public Pair add(Pair b) {}// +
public Pair dec(Pair b) {}// -
public Pair mul(Pair b) {}// *
public Pair div(Pair b) {}// /
public boolean compare(Pair b) {}// >=
}
- solve类的主要方法:完成题目要求的逻辑部分,判断计算表达式是否合法,写入文件,check答案。
public class Solve {
// 将随机生成的表达式转化为后缀表达式,用于后续计算
public static String to_suf(String expression) {}
// 计算后缀表达式 并返回运算顺序
public static Pair calculate(String expression, StringBuilder cal) {}
// check答案
public static void check() {}
// 生成n个表达式,范围为r
public static void Build(int r, int n, PrintWriter out_exp, PrintWriter out_ans) {
//使用hash_set判重
HashSet<String> st = new HashSet<String>();
for (int i = 0; i < n; ++i) {
StringBuilder cal = new StringBuilder("");
String expression = Builder.get_expression(r);
if (expression.equals(" ")) {
System.out.println("Error");
return;
}
Pair ans = calculate(expression, cal);
if (ans.isLessZero() || st.contains(cal.toString())) {
i--;
continue;
}
st.add(cal.toString());
out_exp.println((i + 1) + "." + expression + " =");
out_ans.println(ans.toString());
}
}
}
PSP
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | ||
· Estimate | · 估计这个任务需要多少时间 | 20 | 25 |
Development | 开发 | ||
· Analysis | · 需求分析 (包括学习新技术) | 420 | 500 |
· Design Spec | · 生成设计文档 | 20 | 30 |
· Design Review | · 设计复审 (和同事审核设计文档) | 40 | 35 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 15 | 10 |
· Design | · 具体设计 | 35 | 40 |
· Coding | · 具体编码 | 540 | 600 |
· Code Review | · 代码复审 | 50 | 55 |
· Test | · 测试(自我测试,修改代码,提交修改) | 90 | 100 |
Reporting | 报告 | ||
· Test Report | · 测试报告 | 20 | 30 |
· Size Measurement | · 计算工作量 | 20 | 15 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 30 | 25 |
合计 | 1300 | 1465 |
总结
-
郑梓淳
1.由于之前已经做过wc个人项目,所以这次做起来比较顺手,比如工具的使用等
2.题目生成中括号的插入是直接手动拼接的,如果运算符的个数是随机的,这种做法显然不行,有取巧的成分
3.第一次做GUI,只实现功能,界面的布局较简陋
4.结对的感受:此次的项目交流,除了讨论分工外,其余都是我在问结对伙伴问题,感谢大佬带飞。结对的好处是可以做自己擅长的方面,这样效率会比较高。 -
张强
1.刚接触java,很多地方不熟悉,感觉怎么写怎么报错,很麻烦。不过确实比c++方便。
2.写的時候不够认真,很多地方不够严谨,会出现除0,没化简等情况,都是在后面的测试里找到的问题,改了改又交上git了,提交的次数感觉比较多。
3.结对的感受:互相监督会比较有意愿去学习,遇到问题也可以相互讨论。GUI啥的伙伴很主动去学习,遇到问题也会尽量找方法解决,后面测试找到很多bug,很认真,牛。
以上是关于结对项目-ExpressionBuilder的主要内容,如果未能解决你的问题,请参考以下文章