项目成员:
邓镇港 3117004608
陈嘉欣 3117004604
## 一、Github项目地址:
**[https://github.com/kestrelcjx/operation_expression](https://github.com/kestrelcjx/operation_expression)**
## 二、PSP表格
PSP2.1|Personal Software Process Stages|预估耗时(分钟)|实际耗时(分钟)
-|-|-|-
Planning|计划|30|25
Estimate|估计这个任务需要多少时间|10|12
Development|开发|600|488
Analysis|需求分析|120|150
Design Spec|生成设计文档|30|55
Design Review|设计复审|40|50
Coding Standard|代码规范|20|65
Design|具体设计|60|40
Coding|具体编码|480|460
Code Review|代码复审|30|25
Test|测试(自我测试,修改代码,提交修改)|60|125
Reporting|报告|60|100
Test Report|测试报告|20|35
Size Measurement|计算工作量|10|12
Postmortem & Process Improvement Plan|事后总结, 并提出过程改进计划|120|100
合计||1690|1742
## 三、效能分析
把数字封装成类,每个数字类由整数、分子、分母三部分组成。在类中重新定义加减乘除方法,每个运算方法都会用到通分,约分,将他们写成reduction()方法,gcd()方法。
查重功能实现得比较简单,生成10000条题目所需的时间很短:
![](https://img2018.cnblogs.com/blog/1797681/201910/1797681-20191016221750697-932861992.png)
![](https://img2018.cnblogs.com/blog/1797681/201910/1797681-20191016221800553-856069180.png)

代码覆盖率:

四、设计实现过程

这次的项目需求是自动生成四则运算题目,难点在于随机数的处理和优先级的计算。在阅读完题目并分析需求后,我们先解决的是随机数的生成。因为要求有自然数和真分数,我们用到了一个数字类BasicWork来产生随机数。数字类定义一个数为带分数,分子为零的时候生成一个整数,否则生成一个真分数。这样做的好处是可以将自然数和真分数都统一,在后续的生成和计算过程能够通过调用数字类的方法处理。在数字类中,因为每个随机数都有3个部分组成,我们重新定义了数字类的加减乘除方法。数字类中还包括一个约分方法reduction()对生成的随机数和计算的结果进行处理,确保数字类满足是自然数或真分数的需求。



CreatExpress类用来随机生成式子,用一个随机数来决定式子的操作数个数,根据操作数个数随机生成运算符并连接成四则运算表达式。生成括号这一部分的处理,是通过判断括号出现的正确位置来随机生成。



MainShow主类则是显示出相关的使用信息,接收用户传入的参数。实现生成题目文件和答案文件需求的方法写在主类中,循环生成式子的同时把式子逐条写进题目文件里,并计算出答案写进答案文件。判断表达式重复的过程就是判断计算过程是否重复,这里仅仅是判断结果是否相同,不够完善。生成式子时调用主类的check()方法计算得出答案,并判断答案是否一致进行正确统计。

五、代码说明

对表达式字符串逐个字符判断,将操作数与操作符分隔,同时,将原来的中缀表达式转换为后缀表达式进行计算。

对于将中缀表达式转后缀表达式,进行如下操作:从表达式左到右进行操作,如果是数值,将其放入number数组模拟的栈;如果是操作符,判断symbol数组模拟的栈,如果栈为空或栈顶为左括号,操作符直接进栈,如果栈顶为操作符,根据操作符优先级判断,如果栈顶操作符优先级低,操作符直接进栈,否则,将栈顶操作符放入number数组,继续讨论栈顶元素;如果是左括号,直接进栈;如果是右括号,将栈顶操作符放入number数组,直到栈顶为左括号,括号不放入number数组。最后将symbo模拟的栈中剩余操作符取出放入number数组。

于是,我们便得到后缀表达式,对后缀表达式进行计算,从表达式(number数组)左到右进行操作,如果是操作数,放入stk栈;如果是操作符,将stk栈顶2个元素进行计算,再将结果放入stk栈。最后,stk栈剩余一个操作数,便是表达式结果,运算无误则方法返回结果。在计算过程中,判断是否出现负数,如果出现负数方法返回null值,表示表达式不合法。

	//判断表达式是否合法,是否重复
public static BasicWork check(String s) {
boolean flag = true;
String symbol[] = new String[100];
int sym = 0;
String number[] = new String[100];
int num = 0;
String str = "";
//计算、判断合法、判断重复
for(int i = 0; i < s.length(); i++) {
if(s.charAt(i) == '+' || s.charAt(i) == '-' || s.charAt(i) == '×' || s.charAt(i) == '÷'
|| s.charAt(i) == '(' || s.charAt(i) == ')') {
if(!str.equals("")) {
number[num++] = str;
str = "";
}
if(s.charAt(i) == '+') {
while(sym != 0 && !symbol[sym-1].equals("(")) {
number[num++] = symbol[sym-1];
sym--;
}
symbol[sym++] = "+";
}
else if(s.charAt(i) == '-') {
while(sym != 0 && !symbol[sym-1].equals("(")) {
number[num++] = symbol[sym-1];
sym--;
}
symbol[sym++] = "-";
}
else if(s.charAt(i) == '×') {
while(sym != 0 && (symbol[sym-1].equals("×") || symbol[sym-1].equals("÷"))) {
number[num++] = symbol[sym-1];
sym--;
}
symbol[sym++] = "×";
}
else if(s.charAt(i) == '÷') {
while(sym != 0 && (symbol[sym-1].equals("×") || symbol[sym-1].equals("÷"))) {
number[num++] = symbol[sym-1];
sym--;
}
symbol[sym++] = "÷";
}
else if(s.charAt(i) == '(') {
symbol[sym++] = "(";
}
else if(s.charAt(i) == ')') {
while(sym != 0 && !symbol[sym-1].equals("(")) {
number[num++] = symbol[sym-1];
sym--;
}
if(sym != 0 && symbol[sym-1].equals("(")) sym--;
}
}
else {
str += s.charAt(i);
}
}
BasicWork tempA, tempB;
if(!str.equals("")) number[num++] = str;
while(sym > 0) {
number[num++] = symbol[sym-1];
sym--;
}
// for(int i = 0; i < num; i++) System.out.print(number[i]+" ");
// System.out.println();
Stack<BasicWork> stk = new Stack<BasicWork>();
for(int i = 0; i < num; i++) {
if(number[i].equals("+")) {
tempA = stk.peek();
stk.pop();
tempB = stk.peek();
stk.pop();
tempA = tempB.add(tempA);
stk.push(tempA); }
else if(number[i].equals("-")) {
tempA = stk.peek();
stk.pop();
tempB = stk.peek();
stk.pop();
tempA = tempB.sub(tempA);
stk.push(tempA);
if(tempA.zheng < 0 || tempA.fenzi < 0||tempA.fenmu<0) {
flag = false;
break;
}
}
else if(number[i].equals("×")) {
tempA = stk.peek();
stk.pop();
tempB = stk.peek();
stk.pop();
tempA = tempB.mul(tempA);
stk.push(tempA);
}
else if(number[i].equals("÷")) {
tempA = stk.peek();
stk.pop();
tempB = stk.peek();
stk.pop();
if(tempA.zheng == 0 && tempA.fenzi == 0) {
flag = false;
break;
}
tempA = tempB.div(tempA);
stk.push(tempA);
}
else {
stk.push(BasicWork.toBasicWork(number[i]));
}
}
if(flag == false) return null;
else return stk.peek();
}

随机生成一条式子

	//生成一条式子
public static String express(int limit,int opnum) {
String str = null;
for(int i=0;i<opnum;i++) {
a[i]=new BasicWork(limit);
BasicWork.reduction(a[i]); }
if(opnum == 2) {
str = a[0].toString() + getSymbol() + a[1].toString();
}
else if(opnum == 3) {
int randx = (int)(Math.random()*10);
if(randx == 0)
str = "("+a[0].toString() + getSymbol() + a[1].toString() + ")" + getSymbol() + a[2].toString();
else if(randx == 1)
str = a[0].toString() + getSymbol() + "(" + a[1].toString() + getSymbol() + a[2].toString() + ")";
else
str = a[0].toString() + getSymbol() + a[1].toString() + getSymbol() + a[2].toString(); }
else {
int randx = (int)(Math.random()*30);
if(randx == 0)
str = "(" + a[0].toString() + getSymbol() + a[1].toString() + getSymbol() + a[2].toString() + ")"
+ getSymbol() + a[3].toString();
else if(randx == 1)
str = a[0].toString() + getSymbol() + "(" + a[1].toString() + getSymbol() + a[2].toString() + ")"
+ getSymbol() + a[3].toString();
else if(randx == 2)
str = "(" + a[0].toString() + getSymbol() + "(" + a[1].toString() + getSymbol() + a[2].toString() + "))"
+ getSymbol() + a[3].toString();
else if(randx == 3)
str = a[0].toString() + getSymbol() + "(" + a[1].toString() + getSymbol() + "(" + a[2].toString()
+ getSymbol() + a[3].toString() + "))";
else if(randx == 4)
str = "(" + a[0].toString() + getSymbol() + a[1].toString() + ")" + getSymbol() + "(" + a[2].toString()
+ getSymbol() + a[3].toString() + ")";
else
str = a[0].toString() + getSymbol() + a[1].toString() + getSymbol() + a[2].toString()
+ getSymbol() + a[3].toString();
}
return str; }

数字类BasicWork

public class BasicWork {

	int fenzi;
int fenmu;
int zheng; public BasicWork() { } public BasicWork(int limit) {
// fenzi=(int)(0+Math.random()*(limit-0+1));
// zheng=(int)(0+Math.random()*(limit-0+1));
zheng=0;
fenmu=(int)(1+Math.random()*(limit-1+1));
fenzi=(int)(Math.random()*fenmu*limit);
reduction(this);
} //用于测试
public BasicWork(int a,int b,int c) {
fenzi=a;
fenmu=b;
zheng=c;
} //约分方法
public static void reduction(BasicWork reop) {
int re=gcd(reop.fenzi,reop.fenmu);
re = re == 0 ? 1 : re;
reop.fenzi=reop.fenzi/re;
reop.fenmu=reop.fenmu/re;
//System.out.println(reop.fenzi + " " + reop.fenmu);
try {
if(reop.fenzi>=reop.fenmu) {
reop.zheng=reop.zheng+reop.fenzi/reop.fenmu;
reop.fenzi=reop.fenzi%reop.fenmu;
if(reop.fenzi==0) reop.fenmu=1;
}
} catch (Exception e) {
e.printStackTrace();
}
} public BasicWork add(BasicWork a) {
BasicWork temp=new BasicWork();
temp.zheng=zheng+a.zheng;
temp.fenmu=fenmu*a.fenmu;
temp.fenzi=fenzi*a.fenmu+a.fenzi*fenmu;
//约分
reduction(temp);
return temp;
} public BasicWork sub(BasicWork s) {
BasicWork temp=new BasicWork();
temp.fenzi=fenzi+fenmu*zheng;
temp.zheng=0;
temp.fenmu=fenmu;
s.fenzi=s.fenzi+s.fenmu*s.zheng;
s.zheng=0;
temp.fenzi=temp.fenzi*s.fenmu;
s.fenzi=s.fenzi*temp.fenmu;
temp.fenmu=temp.fenmu*s.fenmu;
temp.fenzi=temp.fenzi-s.fenzi;
//约分
reduction(temp);
return temp;
} public BasicWork mul(BasicWork m) {
BasicWork temp=new BasicWork();
temp.fenzi=fenzi+fenmu*zheng;
temp.zheng=0;
temp.fenmu=fenmu;
m.fenzi=m.fenzi+m.fenmu*m.zheng;
m.zheng=0;
temp.fenmu=temp.fenmu*m.fenmu;
temp.fenzi=temp.fenzi*m.fenzi;
//约分
reduction(temp);
return temp;
} public BasicWork div(BasicWork d) {
d.fenzi+=d.fenmu*d.zheng;
d.zheng=0;
int i;
i=d.fenmu;
d.fenmu=d.fenzi;
d.fenzi=i;
return this.mul(d);
} public boolean equals(BasicWork rhs) {
return (zheng*fenmu+fenzi)*rhs.fenmu == (rhs.zheng*rhs.fenmu+rhs.fenzi)*fenmu;
} private static int gcd(int a,int b) {
if(b==0) return a;
return gcd(b,a%b);
} public String toString() {
String s=new String();
if(fenzi==0) s=zheng+"";
else if(zheng==0) s=fenzi+"/"+fenmu;
else s=zheng+"'"+fenzi+"/"+fenmu;
return s;
} //命令行传入参数转换为整型
public static int toInt(String s) {
int ans=0;
for(int i=0;i<s.length();i++) {
ans=ans*10+s.charAt(i)-'0';
}
return ans;
} public static BasicWork toBasicWork(String str) {
BasicWork temp = new BasicWork();
ArrayList<Integer> ary = new ArrayList<Integer>();
int tmp = 0;
for(int i = 0; i < str.length(); i++) {
if(str.charAt(i) >= '0' && str.charAt(i) <= '9') tmp = tmp*10+str.charAt(i)-'0';
else {
ary.add(tmp);
tmp = 0;
}
}
ary.add(tmp);
if(ary.size() == 1) {
temp.zheng = ary.get(0);
temp.fenzi = 0;
temp.fenmu = 1;
}
else if(ary.size() == 2) {
temp.zheng = 0;
temp.fenzi = ary.get(0);
temp.fenmu = ary.get(1);
}
else {
temp.zheng = ary.get(0);
temp.fenzi = ary.get(1);
temp.fenmu = ary.get(2);
}
return temp;
}
}

六、测试运行

测试-n -r







测试-e -a

这里选用刚刚生成的题目文件和答案文件,答案文件修改几个答案以验证功能









生成10000条题目



经验证,功能正确实现。

七、项目小结

  1. 这次的核心是数字类和转换为后缀表达式去计算答案。数字类将操作数封装成类,在后续的操作中会简单很多,而且代码更简洁易懂。
  2. 初次使用结对编程这种软件开发的方法开发项目,受益匪浅,2个人若是能很好磨合,将达到事半功倍的效果。打代码过程互相鼓励,互相监督,比起自己一个人打代码更有激情。

最新文章

  1. ApacheCommons的Java公共类库(实现如Log这些功能)
  2. 深圳测试研讨会圆满结束,PPT共享
  3. ZendStudio调试配置(XDebug)
  4. iOS - OC 与 Swift 互相操作
  5. CSS3 transition规范的实际使用经验
  6. MIPI DSI 和 D-PHY 初始化序列
  7. C语言中的函数
  8. Free download SKP900 update tool &amp; activation tool
  9. sql2005数据库转换成sql2000
  10. Swift 字符串连接
  11. 总结 xib
  12. 在CentOS 7下试验Drupal 7
  13. HDU 1885 Key Task 国家压缩+搜索
  14. 开始Unity3D参观考察
  15. Dockerfile指令介绍
  16. openstack--10--知识点补充
  17. vue 中结合百度地图获取当前城市
  18. Java编码 蛇形矩阵的构建与遍历输出
  19. 【源码阅读】VS调试mimikatz-改造法国神器mimikatz执行就获取明文密码
  20. 【Ctsc2011】幸福路径

热门文章

  1. 03-cmake语法-变量,字符串
  2. Netty的常用API(二)
  3. LG5338/BZOJ5509/LOJ3105 「TJOI2019」甲苯先生的滚榜 Treap
  4. 四则运算web版需求规格说明书
  5. NeuHub图像垃圾分类api和百度图像识别api
  6. redixdb 基于redis 协议的实时key-value 存储
  7. 洛谷4965 薇尔莉特的打字机(Trie,DP)
  8. [LeetCode] 28. Implement strStr() 实现strStr()函数
  9. linux内核动态调试技术
  10. App.vue 不触发 beforeRouteEnter