java项目的实现过程_结对项目 Java实现

结对成员:宗义澎、闫浩宇

一、GitHub地址

二、PSP:

1

Personal Software Process Stages

预估耗时(分钟)

实际耗时(分钟)

·Planning

·计划

30

40

· Estimate

· 估计这个任务需要多少时间

30

20

·Development

·开发

300

420

· Analysis

· 需求分析

60

50

· Design Spec

· 生成设计文档

30

20

· Design Review

· 设计复审

30

30

· Coding Standard

· 代码规范

100

120

· Design

· 具体设计

60

20

· Coding

· 具体编码

1200

700

· Code Review

· 代码复审

20

20

· Test

· 测试(自我测试,修改代码,提交修改)

200

100

·Reporting

·报告

100

140

· Test Report

· 测试报告

60

50

· Size Measurement

· 计算工作量

20

20

· Postmortem & Process Improvement Plan

· 事后总结, 并提出过程改进计划

40

50

合计

2010

1800

三、效能分析

d950eaa4170e17e434bb25400cf75eb3.png

相较于各路大神的各种神仙速度,这个速度实在是慢(似乎是别人的100倍之多?),究其原因,我认为有以下几点:

1.用了过多的随机数:为了实现题目的随机,我在生成题目的许多地方都用了随机数,比如算式中有几个运算符,每个运算符是什么,括号有几个,在第几个位置。这些随机数会降低程序的运行速度

2.数据结构:为了处理方便,我将题目中所有的数都封装成了自己写的分数类(一个分子一个分母),在计算答案时要先封装成分数类在去计算,这也会降低程序的运算速度。

四、设计实现过程

首先阅读需求分析,可以大致将问题分为两部分,一部分是生成题目,另一部分是验证题目正确性。为了让程序的数据结构清晰,我将程序中的所有数据封装成了一个类,然后给出分数类的计算方法。生成题目和验证题目的内容放在了Util包里,具体情况如下图

a540f33d8af5044b43fe513a53ebbe82.png

其中Main是主类,用于处理各种给定的参数;bean包内是分数类;util包中,FileUtil用于处理文件,Number解决如何计算分数类,Question用于生成随机程序

五、代码和具体思路

这部分分为三部分来说

1.生成题目

题目要求给定一个数作为数字的上限,然后生成不能重复的题目,题目的运算符不能超过三个,最后的结果不能是负数。由于题目要求不能重复,我开始的想法是不用随机数,用顺序的方法生成题目,但这样根本不可行,因为题目的范围太大,生成的题目非常相似,不能拿给小学生做,于是还是采用了随机数的策略。但是用了随机数可能就会出现重复的题目,于是我干脆就找它的充分条件:只要答案重复,就认为两道题重复,这样做的合理性是题目范围非常大,生成重复题目概率本就很小,就不要为它花费太多的时间了。另外还需要解决的是运算符和括号的问题,这个地方上面也提到过,用的是随机数,生成随机数决定题目的类型。至于括号,用的则是穷举法,列举出每种情况可能出现的情况,然后再用随机数决定题目属于哪种情况。这个部分的代码主要都是随机数的调用,代码从略。

2.生成答案

这应该是这个项目最难的地方,给定了一个题目,如何算出它的答案呢?我们知道,题目中有括号,另外还要考虑乘除号比加减号运算等级高,所以不能简单地从左到右解析题目,这个地方的算法用的是之前数据结构学过的一套东西,具体算法为现将给出的中缀表达式题目转化为后缀表达式,然后借助栈来运算后缀表达式。具体到这个项目上,在实现算法的时候,需要把字符串形式的题目先转化为中缀表达式,再转化为后缀表达式,与此同时,题目中的数要封装为分数类,便于进行入栈出栈操作,下面代码显示如何实现这个算法。

/*** 将字符串形式的题目转化为Queue形式的中缀表达式

**/

public static QueueReadString(String question) {

Queue mid = new LinkedList();int son = -1;

String temp= "";for(int x = 0; x

mid.add(c);

}else if(c==')' || c=='+' || c=='-' || c=='×' || c=='÷') {if(son!=-1) {//如果有分子,则说明现在的temp是分母

mid.add(newNum(son,Integer.valueOf(temp)));

temp= "";

son= -1;

}else if(!temp.equals("")) {//若队列内有字符,则temp是分子,分母为1

mid.add(new Num(Integer.valueOf(temp),1));

temp= "";

}

mid.add(c);

}else {//读到数字或者分母号

if(c!='/') {//不是分数号,放到temp队列里

temp = temp+c;

}else {//遇到分数号,队列的东西提出来做分子

son =Integer.valueOf(temp);

temp= "";

}

}

}//把最后一个数搞出来

if(son!=-1) {//如果有分子,则说明现在的temp是分母

mid.add(newNum(son,Integer.valueOf(temp)));

temp= "";

}else if(!temp.equals("")) {//若队列内有字符,则temp是分子,分母为1

mid.add(new Num(Integer.valueOf(temp),1));

temp= "";

}returnmid;

}/*** 将Queue计算为最后结果

* 应输入中缀表达式*/

public staticNum CountQueue(Queue mid) {

Queue after =MidToAfter(mid);

Stack tempStack = new Stack();while(after.peek()!=null) {if(after.peek() instanceof Num) {//操作数直接入栈

tempStack.push(after.poll());

}else{char op = (char)after.poll();

Num n,first,second;switch(op) {case '+':

n=NumberUtil.add((Num)tempStack.pop(),(Num)tempStack.pop());

tempStack.push(n);break;case '-':

first=(Num)tempStack.pop();

second=(Num)tempStack.pop();

n=NumberUtil.sub(second,first);

tempStack.push(n);break;case '×':

n=NumberUtil.mul((Num)tempStack.pop(),(Num)tempStack.pop());

tempStack.push(n);break;case '÷':

first=(Num)tempStack.pop();

second=(Num)tempStack.pop();

n=NumberUtil.div(second,first);

tempStack.push(n);break;

}

}

}

Num answer=(Num)tempStack.pop();returnNumberUtil.normal(answer);

}/*** 将中缀表达式转换为后缀表达式

**/

public static QueueMidToAfter(Queue mid) {

Queue after = new LinkedList();

Stack tempStack = new Stack();while(mid.peek()!=null) {if(mid.peek() instanceof Num) {//操作数直接入队列

after.add(mid.poll());

}else {//符号的判定

char c = (char) mid.poll();if(c=='(') {//左括号直接入栈

tempStack.add(c);

}else if(c==')') {//右括号把栈里的东西全弄到队列里,直到遇到左括号

while (true) {if(tempStack.empty()) {

System.out.println("缺少左括号! ");return null;

}else if ((char)tempStack.peek()=='(') {

tempStack.pop();break;

}else{

after.add(tempStack.pop());

}

}

}//非括号类运算符

else if (!tempStack.empty()) {char peek = (char)tempStack.peek();//当前运算符优先级大于栈顶运算符优先级,或者栈顶为左括号时,当前运算符直接入栈

if(((c=='×' ||c=='÷')&&((peek=='+') || (peek=='-'))) || peek=='(') {

tempStack.push(c);

}//否则,将栈顶的运算符取出并存入队列,然后将自己入栈

else{

after.add(tempStack.pop());

tempStack.push(c);

}

}else{

tempStack.push(c);

}

}

}while(!tempStack.empty()) {

after.add(tempStack.pop());

}returnafter;

}

以上的三个方法可以实现上述算法。

3.检查用户输入

这个部分是由队友完成,我直接把他的实现方法贴上来:

1)打开习题册,创建一个文件选择器,由用户自己选择要练习的习题册。将选择的文件逐行读取,并显示在控制台上。前提是已经有生成过的题目文件在储存盘中。

2)输入答案。由用户自行根据习题册内容输入自己的答案,并以换行为题目分割。同样用文件选择器保存到目标路径下。

3)检查。分别将答案文件与用户用io流读取。逐行比较,并记录错题数与题号。将保存好的错题记录输出。

六、测试运行

输入生成题目的指令:

0efaead37404d857368a571c124dd57b.png

在项目的questionbank中生成了对应文件

1de6f5ea801d348211506a012318ecd4.png

题目文件的具体内容:

3fbcac6ad0ebd7033abc8f2b95a6cad9.png

输入检查的指令

ea4ea49edbf5ca3ea333fcd6f9fb5f21.png

输入指令后成功验证答案

fa2966a4f028d7d3e747164e1636f2f7.png

七、项目小结

通过这个项目,体会到了结对编程的好处,结对编程可以动用两个人的智慧,想到更多的好方法,除此之外,通过这个项目,对之前的Java知识也有了更深的认识。这个项目完成的不好的地方是没有过多的考虑程序运行时间的问题,导致了程序效能极低,以后需要在这方面多下功夫。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值