结对项目
这个作业属于哪个课程 | https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/ |
---|---|
这个作业要求在哪里 | https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/homework/13230 |
这个作业的目标 | 学习合作编程开发项目,继续熟悉运用性能分析工具和PSP表格管理时间,养成良好编程风格 |
成员:许凌铎 3122004540 邓茗骏 3122004517
github仓库地址:https://github.com/xulingduo/partner-program/releases/tag/homework
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 10 | 10 |
· Estimate | · 估计这个任务需要多少时间 | 20 | 20 |
Development | 开发 | 670 | 670 |
· Analysis | · 需求分析 (包括学习新技术) | 60 | 70 |
· Design Spec | · 生成设计文档 | 60 | 60 |
· Design Review | · 设计复审 | 40 | 50 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 60 | 50 |
· Design | · 具体设计 | 60 | 60 |
· Coding | · 具体编码 | 240 | 240 |
· Code Review | · 代码复审 | 30 | 20 |
· Test | · 测试(自我测试,修改代码,提交修改) | 120 | 120 |
Reporting | 报告 | 60 | 60 |
· Test Repor | · 测试报告 | 20 | 30 |
· Size Measurement | · 计算工作量 | 20 | 15 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 20 | 15 |
· 合计 | 740 | 740 |
效能分析
改进性能总共花费大约2小时时间。
思路:将数据结构进行统一,减少复杂度,使代码更易懂。将判断与计算一起进行,减少迭代次数。
Jprofiler性能分析图:
最耗时的函数:
public static String calculate(ArrayList<String> problem) {//计算总函数
properFraction result=null,test;
ArrayList<String> p1=problem;
ArrayList<String> p2=new ArrayList<String>();
ArrayList<String> p3=new ArrayList<String>();
String check,s,t;
//排错
if(problem.size()>=2 && problem.get(0).equals(""))return problem.get(1);
if(!(check=checkExpression(problem)).equals(""))return check;
//计算
for(int i=p1.size()-1;i>=0;i--) {
p2.add(s=p1.get(i));p1.remove(i);
if(s.equals("(")) {
for(int j=p2.size()-1;j>=0;j--) {
p3.add(t=p2.get(j));p2.remove(j);
if(t.equals("(")||t.equals(")"))p3.remove(p3.size()-1);
if(t.equals(")"))break;
}
test=compute(p3);if(!test.error.equals(""))return test.error;
p2.add(properFraction.getString(test));p3=new ArrayList<String>();
}
}
if(p2.size()>1) {
for(int j=p2.size()-1;j>=0;j--) {
p3.add(s=p2.get(j));p2.remove(j);
if(s.equals("(")||s.equals(")"))p3.remove(p3.size()-1);
if(s.equals(")"))break;
}
test=compute(p3);if(!test.error.equals(""))return test.error;
p2.add(properFraction.getString(test));p3=new ArrayList<String>();
}
//返回结果
if(p2.get(0).split(" ").length>=2)result=properFraction.getProperFraction(p2.get(0));
else result=properFraction.toProperFraction(p2.get(0));
return properFraction.print(result);
}
该函数由于是计算模块的主要函数,调用了许多子函数,在运行过程中最耗时。
设计实现过程
·一共5个类:分数类,问题生成类,答案生成类,文件I/O类以及综合这些类的项目主类。
·函数:分数类需要提供加减乘除方法和与字符串互转的方法,问题生成类和答案生成类需要提供对应生成方法,文件I/O类需要提供读写、清除文件等方法。
类关系图:
项目主类主函数流程图:
代码说明
·问题生成函数:起始一个数,再通过一个符号一个操作数生成问题,对问题中可以添加括号的位置进行随机添加。
public static String getProblem(){
ArrayList<String> problem=new ArrayList<String>();
problem.add("");problem.add(getNumber());problem.add("");//获取一个数
int length=getLength();
for(int i=0;i<length;i++) {//通过符号数进行循环
problem.add(getOperator());//获取一个运算符
problem.add("");problem.add(getNumber());problem.add("");//获取一个数
}
if(bracket) {//是否要求括号
String[] brackets= { "(" , ")" };
int test=0,save=1;
for(int i=0;i<(length+1)*2-1;i++) {
if(doRandomBooleanBracket() && !(i%2==0&&test==1) && !(i%2==1&&test==0) && !(i==(length+1)*2-2 && test==0)) {
problem.set(i*2, brackets[test]);test=(test+1)%2;
save++;if(i==0)save=-1;//防止整个式子被括号
if(test==1)i++;//防止一个数被括号
}
}
if(test==1 && save!=-1)problem.set(problem.size()-1, brackets[test]);//加回右括号
if(save==-1)problem.set(0, "");//排错
}
//输出过程可能产生的空字符串
String Problem="";
for(int i=0;i<problem.size();i++) {
Problem=Problem+problem.get(i);
}
return Problem;
}
·答案生成函数:对输入问题进行排错后,检测括号,优先计算括号内式子,过程中报错即返回提示信息,最后返回结果。
public static String calculate(ArrayList<String> problem) {//计算总函数
properFraction result=null,test;
ArrayList<String> p1=problem;
ArrayList<String> p2=new ArrayList<String>();
ArrayList<String> p3=new ArrayList<String>();
String check,s,t;
//排错
if(problem.size()>=2 && problem.get(0).equals(""))return problem.get(1);
if(!(check=checkExpression(problem)).equals(""))return check;
//计算
for(int i=p1.size()-1;i>=0;i--) {
p2.add(s=p1.get(i));p1.remove(i);
if(s.equals("(")) {//计算一个括号内的表达式
for(int j=p2.size()-1;j>=0;j--) {
p3.add(t=p2.get(j));p2.remove(j);
if(t.equals("(")||t.equals(")"))p3.remove(p3.size()-1);
if(t.equals(")"))break;
}
test=compute(p3);if(!test.error.equals(""))return test.error;
p2.add(properFraction.getString(test));p3=new ArrayList<String>();
}
}
if(p2.size()>1) {
for(int j=p2.size()-1;j>=0;j--) {
p3.add(s=p2.get(j));p2.remove(j);
if(s.equals("(")||s.equals(")"))p3.remove(p3.size()-1);
if(s.equals(")"))break;
}
test=compute(p3);if(!test.error.equals(""))return test.error;
p2.add(properFraction.getString(test));p3=new ArrayList<String>();
}
//返回结果
if(p2.get(0).split(" ").length>=2)result=properFraction.getProperFraction(p2.get(0));
else result=properFraction.toProperFraction(p2.get(0));
return properFraction.print(result);
}
测试运行
对主要函数运行时各种可能出现的结果进行断言,确保程序能正常运行。
@Test
void test1() {
String test=answer.calculate(answer.split("(2'1/4÷1+0)×(2×9)-10"));
String answer="30'1/2";
assertEquals(test,answer);
}
@Test
void test2() {
String s;
String[] ss=new String[10];
String[] sss=new String[10];
for(int i=0;i<10;i++) {
s=problem.getProblem();
if(!answer.calculate(answer.split(s)).equals("过程产生负数")) {
ss[i]=answer.calculate(answer.split(s));
sss[i]=s;
}
else i--;
}
for(int i=0;i<ss.length;i++) {
assertEquals(ss[i],answer.calculate(answer.split(sss[i])));
}
}
@Test
void test3() {
String[] argsTest1= {"-n","9","-r","10"};
input_output.clear("Grade.txt");
String[] argsTest2= {"-e","Exercises.txt","-a","Answers.txt"};
Myapp.work(argsTest1);Myapp.work(argsTest2);
assertEquals(input_output.read("Grade.txt"),"Correct:9(1,2,3,4,5,6,7,8,9)\nWrong:0()\n");
}
@Test
void test4() {
String[] argsTest1= {"-n","5","-r","10"};
input_output.clear("Grade.txt");
String[] argsTest2= {"-e","Exercises.txt","-a","Answers.txt"};
Myapp.work(argsTest1);Myapp.work(argsTest2);
assertEquals(input_output.read("Grade.txt"),"Correct:5(1,2,3,4,5)\nWrong:0()\n");
}
@Test
void test5() {
String test=answer.calculate(answer.split("(2'1//4÷1+0)"));
String answer="符号\"//\"无法被识别!";
assertEquals(test,answer);
}
@Test
void test6() {
String test=answer.calculate(answer.split("(2'1/4÷0+0)"));
String answer="出现除以0的情况";
assertEquals(test,answer);
}
@Test
void test7() {
String test=answer.calculate(answer.split("(2'1/0÷1+0)"));
String answer="出现分母为0的情况";
assertEquals(test,answer);
}
@Test
void test8() {
String test=answer.calculate(answer.split("(2'1/6÷1+0*9)"));
String answer="输入的符号\"*\"无法被识别!";
assertEquals(test,answer);
}
@Test
void test9() {
String test=answer.calculate(answer.split("2'1/6÷1+0++1"));
String answer="符号与操作数数量不匹配";
assertEquals(test,answer);
}
@Test
void test10() {
String test=answer.calculate(answer.split("2'1/6÷1+0+1)"));
String answer="出现错误:括号不能正确匹配";
assertEquals(test,answer);
}
@Test
void test11() {
String test=answer.calculate(answer.split("2'1/6÷1+0+1()"));
String answer="括号中无表达式";
assertEquals(test,answer);
}
测试覆盖率为88.3%如下:
测试结果完全符合预期如下:
项目小结
本次项目通过对问题合作探讨和思路分析,成功实现了小学四则运算生成器,运用了此前学习到的性能分析和PSP时间管理,共同提出了对数据结构和算法逻辑的建议,互相学习的同时节省了很多开发时间,对于项目合作及编程框架运用有了更深的经验和体会。