Java实现四则运算生成器
这个作业属于哪个课程 | 计科二班 |
---|---|
这个作业要求在哪里 | 结对项目 |
这个作业的目标 | 熟悉结对编程 |
项目成员
龙新超 3121004921 github链接:龙新超github
艾孜海尔江 3121004900 github链接:海尔江github
PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 15 | 15 |
· Estimate | · 估计这个任务需要多少时间 | 15 | 15 |
Development | 开发 | 715 | 695 |
· Analysis | · 需求分析 (包括学习新技术) | 150 | 150 |
· Design Spec | · 生成设计文档 | 15 | 15 |
· Design Spec | · 设计复审 | 20 | 20 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 20 | 40 |
· Design | · 具体设计 | 60 | 40 |
· Coding | · 具体编码 | 240 | 250 |
· Code Review | · 代码复审 | 120 | 90 |
· Test | · 测试(自我测试,修改代码,提交修改) | 90 | 90 |
Reporting | 报告 | 60 | 50 |
· Test Repor | · 测试报告 | 20 | 20 |
· Size Measurement | · 计算工作量 | 20 | 15 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 20 | 15 |
· 合计 | 790 | 760 |
设计实现
项目结构
1.bean类:Fraction,OperationExpression
2.tool类:DuplicateCheck
3.Service类:CaculateService,CheckService,getEpression
4.入口:OperationPro
设计思路
用Fraction生成分数或者整数(两种都可),实体类OperationExpression类,其熟悉有答案和题目表达式。
题目生成设计思路:
Fraction为基本计算的因子,OperationExpression对象e,用来生成表达式和答案,需要计算服务类的calculate()方法,并对生成的算式进行重复检查。
,### 关键代码
- getExpression
public class getExpression {
private static Random r = new Random();
static List l = new ArrayList();
//获得字符串集合,并生成表达式
public List getExp(OperationExpression e,int limit){
Fraction f1 = new Fraction(r.nextBoolean(),limit);
List stringList = new ArrayList();
List l = new ArrayList();
l.add(f1);
stringList.add(f1.toString());
int a = r.nextInt(3);
do{
express(e,l,stringList,limit);
a--;
}while (a>=0);
Fraction f =CalculateService.calculate(l);
if(f==null){
return null;
}
e.setRes(f);
e.setSl(stringList);
return stringList;
}
static void express(OperationExpression e,List l,List stringList,int limit){
Fraction f = new Fraction(r.nextBoolean(),limit);
int a = r.nextInt(4); //取值范围:[0,4)中的0/1/2/3,随机取运算符
switch (a){ //0:加,1:减,2:乘,3:除
case 0: l.add("+");l.add(f);
stringList.add("+");stringList.add(f.toString());
break;
case 1:
//检查是否存在e1-e2<0的情况,如存在,应调换位置
l.add("-");l.add(f);
stringList.add("-");stringList.add(f.toString());
break;
case 2: l.add("×");l.add(f);
stringList.add("×");stringList.add(f.toString());
break;
case 3: l.add("÷");l.add(f);
stringList.add("÷");stringList.add(f.toString());
break;
default:
out.println("出错");
}
e.setList(l);
}
}
2.CaculateService
public class CalculateService {
/**
* 中序转后序表达式的各种逻辑判断,将判断的结果送入toPostfix()进行各种情况的具体逻辑处理
*
* @param o
* @param number number栈
* @param action action栈
* @return 返回各种情况的symbol
*/
public static int flag(Object o, Stack number, Stack<String> action) {
if (o instanceof Fraction)
return 1;// number
//是操作符
String s = (String)o;
if (s.matches("(\\+)|(\\-)|(\\×)|(\\÷)")) {
if (action.isEmpty()) {
return 2;// action为空
} else if (prior(s, action.peek())) {
return 3;// action不为空,操作符优先级高于栈顶操作符
} else {
return 4;// action不为空,操作符优先级不高于栈顶操作符
}
}
if (s.matches("\\("))
return 5;// 左括号
if (s.matches("\\)"))
return 6;// 右括号
//都不是
return 0;
}
/**
* 中序变为后序
* @param list
* @return
*/
public static Stack toPostFix(List list){
Stack num = new Stack();
Stack<String> action = new Stack<>();
int op = 0;
for (Object o : list) {
op = flag(o, num, action);
switch (op) {
case 1://数字直接入栈
num.push(o);
break;
case 2://操作符栈为空直接入栈
action.push((String) o);
break;
case 3://当前操作符比栈顶操作符优先级高入栈
action.push((String) o);
break;
case 4:
//弹出所有比当前操作符优先级高的,直到遇到左括号或者为空
while (!action.empty() && action.peek()!="(" && !prior((String)o,action.peek())){
num.push(action.pop());//action弹栈并压入number
}
action.push((String) o);//操作符压栈
break;
case 5://左括号无条件入操作栈
action.push((String) o);
break;
case 6:
first: while (!action.isEmpty()) {//action弹栈并压入number栈直到遇到左括号
String temp = action.pop();
if (temp.equals("(")) {
break first;
} else {
num.push(temp);
}
}
break;
default:
break;
}
}
Stack temp = new Stack();
//将剩下的操作符压入number栈中
for (String s : action) {
num.push(s);
}
//反序
for (Object o : num) {
temp.push(o);
}
return temp;
}
/**
* 判断操作符和栈顶操作符的优先级
* @param s1 操作符
* @param s2 栈顶操作符
* @return 优先级
*/
public static Boolean prior(String s1, String s2) {
if (s2.matches("\\(")) //任何操作符的优先级高于左括号
return true;
if (s1.matches("(\\×)|(\\÷)") && s2.matches("(\\+)|(\\-)"))
return true;
return false;
}
public static Fraction calculate(List list){
Stack stack = toPostFix(list);//转为后序表达
Stack<Fraction> newStack = new Stack();//存数字
for (Object o : stack) {
if(o instanceof Fraction){
//是数字
newStack.push((Fraction) o);
}else {
//是操作符
if(newStack.size()<2){
//遇到操作符时栈内元素至少为2
//TODO 可以报错
break;
}
Fraction a = newStack.pop();
Fraction b = newStack.pop();
switch ((String) o) {
case "+":
newStack.push(b.add(a));
break;
case "-":
Fraction fraction = b.sub(a);
//TODO 可以改为抛出减法异常
if(fraction.getNom()<=0||fraction.getDom()<=0){
return null;
}
newStack.push(fraction);//计算结果并压栈,注意顺序
break;
case "×":
newStack.push(b.muti(a));
break;
case "÷":
newStack.push(b.div(a));
break;
default:
break;
}
}
}
return newStack.pop();
}
}
3.DuplicateService
public class DuplicateCheck {
//l为判断的表达式集合,allList是已经存在的所有表达式集合
public boolean DuCheck(List l, List allList){
Iterator it = allList.iterator();
while (it.hasNext()){
List L = (List) it.next();
if(CheckList(l,L)) return true;
}
return false;
}
/*
*判断两个String类型的List是否完全相同
*大小一样,所有元素互相含有,元素顺序可以不一致
*/
//l1是l的形参,l2是allList中某个元素的形参
boolean CheckList(List l1,List l2){
if (l1 == l2) {
return true;
}
if (l1 == null && l2 == null)
{
return true;
}
if (l1 == null || l2 == null)
{
return false;
}
if (l1.size() != l2.size())
{
return false;
}
if (l1.containsAll(l2) && l2.containsAll(l1))
{
return true;
}
return false;
}
}
性能分析
内存占用情况
总体性能
测试
测试代码
public class test {
@Test
public void test1(){
OperationPro.mainGenerate(10,10000);
}//生成10内的10000道题目
@Test
public void test2(){//检查答案
OperationPro.mainCheck("D:\\gitcode\\acofGDUT\\3121004921\\homework2\\Answer1.txt","D:\\gitcode\\acofGDUT\\3121004921\\homework2\\Answer.txt");
}
@Test
public void test3(){//生成10内的数的表达式
getExpression getExpression = new getExpression();
List exp = getExpression.getExp(new OperationExpression(null, null, null), 10);
System.out.println(exp);
}
@Test
public void test5(){//两个6内的分数相除
Fraction a = new Fraction(true,6);
Fraction b = new Fraction(true,6);
System.out.println(a.sub(b));
}
@Test
public void test6(){//10内的整数减去8内的分数
Fraction a = new Fraction(false,10);
Fraction b = new Fraction(true,8);
System.out.println(a.div(b));
}
@Test
public void test7(){//两个8以内的整数相加
Fraction a = new Fraction(false,8);
Fraction b = new Fraction(false,8);
System.out.println(a.add(b));
}
@Test
public void test8(){ // 两个8以内的整数相除
Fraction a = new Fraction(false,8);
Fraction b = new Fraction(false,8);
System.out.println(a.sub(b));
}
@Test
public void test9(){
System.out.println(CalculateService.prior("÷", "-"));
} //判断除法和减法的优先级
}
测试运行结果
覆盖率
文档生成情况
复制一份答案Answer1.txt 修改一部分答案,检查Grade文档
项目小结
龙新超:一起编写代码,能够迅速发现和纠正彼此的错误。这种实时反馈有助于提高代码质量,而且我学到了很多新知识,因为各自带来了不同的观点和经验。此外,两人在讨论设计和实现方案时能够做到更全面的思考,这也有助于我们制定更好的解决方案。
海尔江:两个人一起工作,能够共同解决问题,一起思考最佳实践,并发现彼此的盲点。这种协作方式加速了问题的诊断和解决,两个人互相交流和学习的经验也非常有价值。尽管在一开始可能感到有些不习惯,但我认为结对编程是一种提高代码质量和工作效率的重要实践。