结对项目——四则运算
这个作业属于哪个课程 | 软件工程 |
---|---|
这个作业要求在哪里 | 课程要求 |
代码文件 | GitHub |
这个作业的目标 | 在结对编程中总结经验和教训 |
参与者 | 黄梦莎 3221005240,古丽波斯旦·艾比布拉 3221005328 |
1. PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 20 | 30 |
· Estimate | · 估计这个任务需要多少时间 | 900 | 960 |
Development | 开发 | 900 | 1080 |
· Analysis | · 需求分析 (包括学习新技术) | 10 | 10 |
· Design Spec | · 生成设计文档 | 60 | 60 |
· Design Review | · 设计复审 (和同事审核设计文档) | 10 | 10 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 10 | 10 |
· Design | · 具体设计 | 30 | 60 |
· Coding | · 具体编码 | 300 | 360 |
· Code Review | · 代码复审 | 50 | 30 |
· Test | · 测试(自我测试,修改代码,提交修改) | 100 | 120 |
Reporting | 报告 | 60 | 90 |
· Test Report | · 测试报告 | 40 | 50 |
· Size Measurement | · 计算工作量 | 10 | 10 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 10 | 20 |
合计 | 2510 | 2900 |
2. 主要函数
- subject() // 生成表达式
- change(int n, int d) // 将假分数转换为真分数,第一个参数为分子,第二个为分母
- ifExist(List s) // 用于检查是否有相同的题目,并处理
- save(List s) // 用于将题目存入当前目录下的Exercises.txt文件
- answer(List a) // 用于将题目答案存入当前目录下的Answer.txt文件
- check(File e, File a,File g) // 用于对给定的题目文件和答案文件判断答案文件中的对错并统计至Grade.txt
- count(List list) // 构建树计算表达式,并存入题目列表
3. 部分代码
产生随机数
// 产生随机数
public static String createnum() {
ThreadLocalRandom rand = ThreadLocalRandom.current();
int a = rand.nextInt(range);
int b = rand.nextInt(range);
if (a == 0) {
a += (int) (Math.random()*range + 1);
}
if (b == 0) {
b += (int) (Math.random()*range + 1);
}
if (b == range) {
b = b-1;
}
num n = new num(a,b);
easy(n);
if (n.a == n.b) {
return Integer.toString(1);
}
if (n.b == 1) {
return Integer.toString(n.a);
}
System.out.println(n.tostring());
return n.tostring();
}
生成随机运算符
// 随机产生运算符
public static String createopr1() {
return opr[(int) (Math.random()*4)];
}
// 只产生 x 或 ÷
public static String createopr2() {
return opr[(int) (Math.random()*2 + 2)];
}
// 只产生 + 或 -
public static String createopr3() {
return opr[(int) (Math.random()*2)];
}
// 处理括号无意义情况
public static void checkbkt(int bkt_s, int bkt_e,List<String>exp) {
String f;
String s;
String t;
if (bkt_e - bkt_s == 1) {
if (bkt_s == 1) {
f = exp.get(2);
s = exp.get(5);
if (!(f.equals("+") || (f.equals("-"))) && !(s.equals("x") || s.equals("÷"))) {
exp.set(2, createopr3());
exp.set(5, createopr2());
}
} else if (bkt_s == 2) {
f = exp.get(1);
s = exp.get(4);
if (exp.size()<8) {
if ((f.equals("+") || (f.equals("-"))) && (s.equals("x") || (s.equals("÷")))) { // 排除 a + ( b x c ) 此类括号无意义的情况
exp.set(1, createopr2());
}
}
} else if (bkt_s == 3) {
f = exp.get(3);
s = exp.get(6);
if ((f.equals("+") || (f.equals("-"))) && (s.equals("x") || s.equals("÷"))) {
exp.set(3, createopr2());
} else if (((f.equals("+") || f.equals("-"))) && (s.equals("+") || s.equals("-"))) {
exp.set(3, createopr2());
}
}
} else {
if (bkt_s == 1) {
f = exp.get(2);
s = exp.get(4);
t = exp.get(7);
if (!(t.equals("x") || t.equals("÷"))) {
exp.set(7, createopr2());
if (f.equals(s) && (f.equals("+") || f.equals("÷"))) {
exp.set(4, createopr2());
}
}
} else if (bkt_s == 2) {
f = exp.get(1);
s = exp.get(4);
t = exp.get(6);
if ((f.equals("+") || f.equals("-")) && (t.equals("x") || t.equals("÷"))) {
exp.set(1, createopr2());
}
}
}
}
计算表达式
// 构建树计算表达式,并存入题目列表
public static String count(List<String> list) {
Stack<Node> a1 = new Stack<>(); // 数值栈
Stack<String> b = new Stack<>(); // 未处理的运算符栈
// Stack<Node> c = new Stack<>(); // 处理后的运算符栈
for (int i=0; i<list.size(); i++) {
String string = list.get(i);
if (!isop(string)) {
// 当前指针为数值
a1.push(new Node(string, null, null, null));
} else {
//比较栈顶符号与当前符号的优先级
while (!b.isEmpty() && !(string.equals("(") || (prefer(string)==2 && prefer(b.peek())==1) ||
(!string.equals(")") && b.peek().equals("(")))) {
String symbol = b.pop();
if (symbol.equals("(") && string.equals(")")) {
break;
}
push(symbol, a1);
}
//如果符号不是")"就进栈
if (!string.equals(")")) {
b.push(string);
}
}
}
while (!b.isEmpty()) {
push(b.pop(), a1);
}
negative(a1);
return a1.peek().result;
}
// 处理负数
public static void negative(Stack<Node> c) {
if (!c.isEmpty()) {
for (int i=0; i<c.size(); i++) {
Node n = c.get(i);
if (n.op.equals("-")) {
num l = new num(n.left.result);
num r = new num(n.right.result);
if (l.a*r.b < r.a*l.b) {
Node m = n.left;
n.left = n.right;
n.right = m;
n.setResult();
}
}
}
}
}
public static void push(String op, Stack<Node> a) {
if (!op.equals("(")) {
Node r = a.pop();
Node l = a.pop();
Node o = new Node(null, r, l, op);
o.result = count(r.result, l.result, op);
a.push(o);
}
}
public static String count(String r, String l, String op) {
num left = new num(l);
num right = new num(r);
switch (op) {
case "+":
return left.add(right).tostring();
case "-":
return left.sub(right).tostring();
case "x":
return left.mul(right).tostring();
case "÷":
return left.div(right).tostring();
}
return null;
}
static class Node {
String result;
Node right;
Node left;
String op;
public Node(String result, Node right, Node left, String op) {
this.result = result;
this.right = right;
this.left = left;
this.op = op;
}
public void setResult() {
if (op!=null) {
result = count(right.result, left.result, op);
}
}
public void changelr() {
Node m = left;
right = left;
left = m;
}
}
public static int prefer(String op) { // 判断运算符优先级
if (op.equals("-") || op.equals("+")) {
return 1;
} else if (op.equals("x") || op.equals("÷")) {
return 2;
} else {
return 3;
}
}
public static boolean isop(String op) { // 判断是否为运算符
if (op.equals("+") || op.equals("-")
|| op.equals("x") || op.equals("÷")
|| op.equals("(") || op.equals(")")) {
return true;
} else {
return false;
}
}
// 检查题目列表中是否有相同的题目,并处理
static void ifExist(ArrayList<String> s) {
for (int i=0; i<s.size(); i++) {
for (int j=i+1; j<s.size();) {
Stack<Node> f = e1.get(i);
Stack<Node> se = e1.get(j);
if (f.size() == se.size() && equals(f.pop(), se.pop())) {
s.remove(j);
} else {
j++;
}
}
}
}
static boolean equals(Node f, Node s) {
if (fullequals(f, s)) {
return true;
} else if (f.op.equals(s.op) && f.result.equals(s.result)){
if (change(f, s)) {
return true;
} else {
return equals(f.left, s.left) && equals(f.right, s.right);
}
}
return false;
}
static boolean change(Node f, Node s) {
if (f.op.equals("+") || f.op.equals("x")) {
f.changelr();
f.setResult();
if (nodeequal(f.left, s.left) && nodeequal(f.right, s.right)) {
return true;
}
}
return false;
}
static boolean nodeequal(Node f, Node s) {
return f.op.equals(s.left.op) && f.result.equals(s.left.result);
}
static boolean fullequals(Node f, Node s) {
if (f.op.equals(s.op) && f.result.equals(s.result)) {
return fullequals(f.left, s.left) && fullequals(f.right, s.right);
}
return false;
}
统计答案
static void check(File e, File a, File g) {
// 用于对给定的题目文件和答案文件判断答案文件中的对错并统计
File g1 = new File("./src/Grade.txt");
if (!g1.exists()){
try {
g1.createNewFile();
} catch (IOException e1) {
e1.printStackTrace();
}
}
try (BufferedReader exReader = new BufferedReader(new FileReader(e));
BufferedReader anReader = new BufferedReader(new FileReader(a));
BufferedWriter gradeWriter = new BufferedWriter(new FileWriter(g1))
) {
String ex, an;
int c = 0, w = 0;
StringBuilder correct = new StringBuilder("Correct: %d (");
StringBuilder wrong = new StringBuilder("Wrong: %d (");
while ((ex = exReader.readLine()) != null && (an = anReader.readLine()) != null) {
int exPoint = ex.indexOf(".");
int anPoint = an.indexOf(".");
if (exPoint != -1 && anPoint != -1) {
int i = Integer.valueOf(ex.substring(0,exPoint).trim());
String expression = ex.substring(exPoint + 2);
String[] exp = expression.split(" ");
List<String> al = new ArrayList<String>();
al = Arrays.asList(exp);
String realanswer = count(al);
String answer = an.substring(anPoint + 2);
if (realanswer.equals(answer.toString())) {
c++;
correct.append(" ").append(i);
if (c % 20 == 0) {
correct.append("\n");
}
} else {
w++;
wrong.append(" ").append(i);
if (w % 20 == 0) {
wrong.append("\n");
}
}
}
}
gradeWriter.write(String.format(correct.append(" )\n").toString(),c));
gradeWriter.write(String.format(wrong.append(" )\n").toString(),w));
gradeWriter.flush();
} catch (IOException e1) {
e1.printStackTrace();
}
}
4. 测试
-
单元测试
-
单元测试代码覆盖率
-
单元测试代码
-
Windows下运行jar包测试
-
输出
- 效能分析
5. 项目小结
这是我们双方第一次尝试二人项目,总体来说比较成功。我们先是对PSP表的预计时间进行讨论,同时确定项目流程,有不足的点在于分工不够明确,导致在编码过程出现重复编码的情况,最终采用了算法复杂度更低的算法。总体来说和古丽的合作十分愉快,她是一个很细心的合作伙伴,在许多细节处会给我意见和建议,我们在这次的二人项目中收获满满。