结对项目

结对编程(JAVA实现)

这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/Networkengineering1834
这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/Networkengineering1834/homework/11148
这个作业的目标 实现一个自动生成小学四则运算题目的命令行程序

项目成员

郭永轩(3118005272) 龚广健(3118005271)

GitHub地址

https://github.com/wobushidamowang/ForCalculate

PSP

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 80 120
· Estimate · 估计这个任务需要多少时间 80 120
Development 开发 1440 1530
· Analysis · 需求分析 (包括学习新技术) 40 50
· Design Spec · 生成设计文档 20 15
· Design Review · 设计复审 (和同事审核设计文档) 20 15
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 20 20
· Design · 具体设计 60 60
· Coding · 具体编码 900 960
· Code Review · 代码复审 30 30
· Test · 测试(自我测试,修改代码,提交修改) 350 380
Reporting 报告 100 110
· Test Report · 测试报告 40 60
· Size Measurement · 计算工作量 20 20
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 40 30
合计 1620 1760

性能分析

各数据类型占用内存

各模块消耗的时间

以上是进行测试的性能测试图,ArrayList类型内存占用最高,查重模块时间消耗最多

2s内完成10个测试,运行速度还是挺快的

设计实现过程

1.设计流程图

​ 生成题目流程图

​ 算术表达式中序变后序流程图

2.项目目录

实体类:分数类Fraction,算术表达式类OperationExpression

工具类:查重类DuplicateCheck

服务类:计算类CalculateService,答案判错类CheckService,表达式生成类getExpression

项目入口:OperationPro

3.关键设计思路

考虑到分数的实现,因为java不存在可以处理分数运算的类,所以应将整数与分数作为一个对象来处理,即整数就是分母为1的分数。也因此做了实体类Fraction

受到Fraction启发,做了实体类OperationExpression,它的属性包含了表达式及其答案

题目生成设计思路:

于是就可以实例化一个OperationExpression对象e,运用getExpression类的getExp方法生成一个表达式设置为e的表达式,getExp()方法中调用了CalculateService的calculate()计算方法得到答案设置为e的答案。calculate()方法中产生了负数则会返回空值,可根据这个空值判断表达式是否存在负数,若存在则丢弃重新生成。其后,还会经过DuplicateCheck类的查重。通过上述步骤,即可生成一道题目。

关键代码说明

表达式生成类getExpression

import bean.Fraction;
import bean.OperationExpression;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import static java.lang.System.out;

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); // [0,3),取值为0/1/2,随机生成运算符个数
        do{
            express(e,l,stringList,limit);
            a--;
        }while (a>=0);

        Fraction f =CalculateService.calculate(l);
        if(f==null){
            return null;
        }
        e.setRes(f);
        e.setStringList(stringList);
        return stringList;
    }

    static void express(OperationExpression e,List l,List stringList,int limit){
        Fraction f = new Fraction(r.nextBoolean(),limit);
        //out.println(f.toString()+"f..");

        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:
                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);
    }
}

计算类CalculateService

import bean.Fraction;

import java.util.List;
import java.util.Stack;

/**
 * 提供计算服务
 */
public class CalculateService {
    /**
     * 中序变为后序
     * @param list
     * @return
     */
    public static Stack toPostFix(List list){
        Stack number = new Stack();
        Stack<String> action = new Stack<>();
        int symble = 0;
        for (Object o : list) {
            symble = flag(o, number, action);
            switch (symble) {
                case 1://数字直接入栈
                    number.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())){
                        number.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 {
                            number.push(temp);
                        }
                    }
                    break;
                default:
                    break;
            }
        }

        Stack temp = new Stack();
        //将剩下的操作符压入number栈中
        for (String s : action) {
            number.push(s);
        }
        //反序
        for (Object o : number) {
            temp.push(o);
        }
        return temp;
    }
    /**
     * 中序转后序表达式的各种逻辑判断,将判断的结果送入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 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.getNominator()<=0||fraction.getDenominator()<=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();
    }
}

查重类DuplicateCheck

package utils;

import java.util.Iterator;
import java.util.List;


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;
    }
}

答案判错类CheckService

import java.io.*;
import java.util.HashMap;
import java.util.Map;

public class CheckService {

   /*
    * 用来检查用户结果的正确性.
    */
   public static Map<String,String> Check(String checked,String answer) throws IOException{
      Map<String,String> m = new HashMap<String,String>();
      String error = "";
      String correct = "";
      int errornum = 0;
      int correctnum = 0;
      
      String str1 = "";
      String str2 = "";
      
//    checked = "D:\\资料\\1.txt";
//    answer = "D:\\资料\\2.txt";
      File checkedfile=new File(checked);
      FileInputStream input1=new FileInputStream(checkedfile);
      BufferedReader reader1=new BufferedReader(new InputStreamReader(input1));
      
      File answerfile=new File(answer);
      FileInputStream input2=new FileInputStream(answerfile);
      BufferedReader reader2=new BufferedReader(new InputStreamReader(input2));
      
      
      while((str1=reader1.readLine())!=null&&(str2=reader2.readLine())!=null){
         if(!str1.trim().equals(str2.trim())){
//          System.out.println(str1);
            String[] str = str1.split("\\.");
            error = error + str[0]+ ",";
            errornum ++ ;
         }else {
//          System.out.println(str1);
            String[] str = str1.split("\\.");
            correct = correct + str[0] + ",";
            correctnum ++;
         }
         
      }
      
      if(error.equals("")){
         error = "Wrong: " + errornum + "";
      }else {
         error = "Wrong: " + errornum + "(" + error.substring(0,error.length()-1) + ")";
      }
      if(correct.equals("")){
         correct = "Correct: " + correctnum + "";
      }else {
         correct = "Correct: " + correctnum + "("+correct.substring(0, correct.length()-1)+")";
      }
//    System.out.println(error);
//    System.out.println(correct);
      m.put("wrong", error);
      m.put("correct", correct);
      return m;
      }
   
}

测试运行

项目小结

测试运行

import bean.Fraction;
import bean.OperationExpression;
import org.junit.Test;

import java.util.List;

public class test {
    @Test
    public void test1(){
        OperationPro.mainGenerate(10,10);
    }
    @Test
    public void test2(){
        OperationPro.mainCheck("D:\\developer\\ForCalculate\\Answer1.txt","D:\\developer\\ForCalculate\\Answer.txt");
    }
    @Test
    public void test3(){
        getExpression getExpression = new getExpression();
        List exp = getExpression.getExp(new OperationExpression(null, null, null), 10);
        System.out.println(exp);
    }
    @Test
    public void test4(){
        getExpression getExpression = new getExpression();
        List exp = getExpression.getExp(new OperationExpression(null, null, null), 3);
        System.out.println(exp);
    }
    @Test
    public void test5(){
        Fraction a = new Fraction(true,10);
        Fraction b = new Fraction(true,10);
        System.out.println(a.sub(b));
    }
    @Test
    public void test6(){
        Fraction a = new Fraction(false,20);
        Fraction b = new Fraction(true,10);
        System.out.println(a.div(b));
    }
    @Test
    public void test7(){
        Fraction a = new Fraction(false,10);
        Fraction b = new Fraction(false,10);
        System.out.println(a.add(b));
    }
    @Test
    public void test8(){
        Fraction a = new Fraction(false,10);
        Fraction b = new Fraction(false,10);
        System.out.println(a.sub(b));
    }
    @Test
    public void test9(){
        System.out.println(CalculateService.prior("+", "-"));
    }
    @Test
    public void test10(){
        getExpression getExpression = new getExpression();
        List exp = getExpression.getExp(new OperationExpression(null, null, null), 3);
        System.out.println(CalculateService.toPostFix(exp));
    }
}

运行结果

运行test1

查看Exersises.txt,成功生成题目:

查看Answers.txt,答案:

复制一份存为Answers1.txt,修改其中0,4,6号的答案,运行test2

项目小结

这次的结对编程,让我们认识到了团队的力量,刚开始的时候效率其实不高,但后面经过不断讨论交流,结对编程这种方式也展现出它高效的一面。还是感觉写代码前的工作没有做到位,如果能把每个人的工作安排到位,编程效率应该可以更高。而且这次的查重思路并没有完全解决问题。

posted @ 2020-10-12 23:42  slowyslowyslowy  阅读(120)  评论(0编辑  收藏  举报