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()方法,并对生成的算式进行重复检查。
,### 关键代码

  1. 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文档

项目小结

龙新超:一起编写代码,能够迅速发现和纠正彼此的错误。这种实时反馈有助于提高代码质量,而且我学到了很多新知识,因为各自带来了不同的观点和经验。此外,两人在讨论设计和实现方案时能够做到更全面的思考,这也有助于我们制定更好的解决方案。
海尔江:两个人一起工作,能够共同解决问题,一起思考最佳实践,并发现彼此的盲点。这种协作方式加速了问题的诊断和解决,两个人互相交流和学习的经验也非常有价值。尽管在一开始可能感到有些不习惯,但我认为结对编程是一种提高代码质量和工作效率的重要实践。

posted @ 2023-09-27 16:16  龙新超  阅读(82)  评论(0编辑  收藏  举报