结对项目

这个作业属于哪个课程 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时间管理,共同提出了对数据结构和算法逻辑的建议,互相学习的同时节省了很多开发时间,对于项目合作及编程框架运用有了更深的经验和体会。

posted @ 2024-09-28 19:58  许凌铎  阅读(16)  评论(0编辑  收藏  举报