Java实现四则运算生成器

发布时间 2023-09-27 16:23:02作者: 龙新超
这个作业属于哪个课程 计科二班
这个作业要求在哪里 结对项目
这个作业的目标 熟悉结对编程

项目成员

龙新超 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

设计实现

项目结构


实体类:分数整数Fraction类,表达式OperationExpression
工具类:DuplicateCheck
服务类:CaculateService,CheckService,getEpression
入口:OperationPro

设计思路

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

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

题目生成设计思路:

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

关键代码

  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); // [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:
                //检查是否存在e1-e2<0的情况,存在则应调换位置
//                out.println(l+"测试");
//                out.println(stringList+"测试");
                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 {
    /**
     * 中序变为后序
     * @param list
     * @return
     */
    public static Stack toPostFix(List list){
        Stack num = new Stack();
        Stack<String> action = new Stack<>();
        int symble = 0;
        for (Object o : list) {
            symble = flag(o, num, action);
            switch (symble) {
                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;
    }
    /**
     * 中序转后序表达式的各种逻辑判断,将判断的结果送入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){
                    
                    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);
               
                        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();
    }
}

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);
    }
    @Test
    public void test2(){
        OperationPro.mainCheck("D:\\gitcode\\acofGDUT\\3121004921\\homework2\\Answer1.txt","D:\\gitcode\\acofGDUT\\3121004921\\homework2\\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,6);
        Fraction b = new Fraction(true,6);
        System.out.println(a.sub(b));
    }
    @Test
    public void test6(){
        Fraction a = new Fraction(false,10);
        Fraction b = new Fraction(true,8);
        System.out.println(a.div(b));
    }
    @Test
    public void test7(){
        Fraction a = new Fraction(false,8);
        Fraction b = new Fraction(false,8);
        System.out.println(a.add(b));
    }
    @Test
    public void test8(){
        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("+", "-"));
    }
    @Test
    public void test10(){
        getExpression getExpression = new getExpression();
        List exp = getExpression.getExp(new OperationExpression(null, null, null), 3);
        System.out.println(CalculateService.toPostFix(exp));
    }

测试运行结果

覆盖率

文档生成情况


复制一份答案Answer1.txt 修改一部分答案,检查Grade文档

项目小结

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