结对项目-ExpressionBuilder

Posted zhengzichun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了结对项目-ExpressionBuilder相关的知识,希望对你有一定的参考价值。

ExpressionBuilder

合作者:张强 & 郑梓淳

Github项目地址

项目要求

实现一个自动生成小学四则运算题目的命令行程序(也可以用图像界面,具有相似功能)。

操作数:

  • 自然数:0, 1, 2, …。
  • 真分数:1/2, 1/3, 2/3, 1/4, 1’1/2, …。

符号:

  • 运算符:+, ?, ×, ÷。
  • 括号:(, )。
  • 等号:=。

分隔符:

  • 空格(用于四则运算符和等号前后)。
功能
  • 生成表达式
  • 显示答案
  • 批改答案
要求
  • 可以控制生成题目的数量
  • 可以控制操作数数和答案的大小
  • 题目不重复
  • 整个运算过程不出现负数
  • 运算符数量小于等于3
  • 支持一万道题目的生成

设计实现过程

graph TD Myapp--> Frame Frame--> JTextFieldHintListener Frame--> Solve Solve--> Bulider Solve--> Pair
  • Frame:实现GUI
  • JTextFieldHintListener:文本框提示内容
  • Builder:随机生成表达式
  • Solve:计算表达式值,检查答案等
  • Pair:分数计算类,实现了分数的加、减、乘、除、小于等

功能介绍

主界面
技术图片
没有输入参数n或者r
技术图片
生成n条题目,题目和答案范围为[0,r)

技术图片

在右边框中作答,并检查答案(答案数量小于题目时则视为未作答,不记录错误)
前十道题手动计算并输入,后十道题输入-1,可以看出程序是正确的

技术图片

在题目中显示答案

技术图片

效能分析

技术图片

复杂度
  • 表达式生成是随机生成操作数和运算符进行组合,线性复杂度。
    计算为转化为后缀表达式后计算,线性复杂度。
    判重是使用在计算時候记录下来的各个中间数和符号的计算顺序,用字符串hash和hashset判重,也是线性复杂度。
实际

生成10k条表达式的时间为0.5-0.7s左右,
100k为4-5s左右,
1000k为47s-50s左右。

分析

从用时上看基本是符合线性复杂度的。
这么花时间的最要原因是随机生成的表达式合法率不高,
是因为表达式生成为随机的,最后还加了判断答案本身和答案的分母是否有超过限制r,不通过则直接舍弃,所以合法的表达式并不多。
如果仅判断答案是否小于0,则100k为1s,1000k为7-8s,少了80%的时间,合法表达式会多很多,基本没有冲突。

不过如果仅生成1w条,则输出时间会比表达式生成的时间多,所以0.5s感觉还可以接受。。不过有优化空间

部分代码说明

  • Bulider类:用于随机产生表达式
        //是否插入括号
        int flag = r1.nextInt(2);
        //随机生成1-3个运算符                                    
        int computeNum = r1.nextInt(3);                              
        switch (computeNum) {
            case 0:
                expression.append(s[0] + " " + ch[0] + " " + s[1]);
                break;
            case 1:
                if (flag == 1) {
                     //括号插入位置随机
                    int index = r1.nextInt(2);                       
                    if (index == 0) {
                        expression.append(
                                "(" + " " + s[0] + " " + ch[0] + " " + s[1] + " " + ")" + " " + ch[1] + " " + s[2]);
                    } else {
                        expression.append(
                                s[0] + " " + ch[0] + " " + "(" + " " + s[1] + " " + ch[1] + " " + s[2] + " " + ")");
                    }
                } else {
                    expression.append(s[0] + " " + ch[0] + " " + s[1] + " " + ch[1] + " " + s[2]);
                }
                break;
            case 2:
                if (flag == 1) {
                     //括号插入位置随机
                    int index = r1.nextInt(6);                                           
                    switch (index) {
                        case 0:
                            expression.append("(" + " " + s[0] + " " + ch[0] + " " + s[1] + " " + ")" + " " + ch[1]
                                    + " " + "(" + " " + s[2] + " " + ch[2] + " " + s[3] + " " + ")");
                            break;
                        case 1:
                            expression.append("(" + " " + s[0] + " " + ch[0] + " " + s[1] + " " + ")" + " " + ch[1]
                                    + " " + s[2] + " " + ch[2] + " " + s[3]);
                            break;
                        case 2:
                            expression.append("(" + " " + s[0] + " " + ch[0] + " " + s[1] + " " + ch[1] + " " + s[2]
                                    + " " + ")" + " " + ch[2] + " " + s[3]);
                            break;
                        case 3:
                            expression.append(s[0] + " " + ch[0] + " " + "(" + " " + s[1] + " " + ch[1] + " " + s[2]
                                    + " " + ")" + " " + ch[2] + " " + s[3]);
                            break;
                        case 4:
                            expression.append(s[0] + " " + ch[0] + " " + "(" + " " + s[1] + " " + ch[1] + " " + s[2]
                                    + " " + ch[2] + " " + s[3] + " " + ")");
                            break;
                        case 5:
                            expression.append(s[0] + " " + ch[0] + " " + s[1] + " " + ch[1] + " " + "(" + " " + s[2]
                                    + " " + ch[2] + " " + s[3] + " " + ")");
                            break;
                        default:
                            expression.append(" ");
                    }
                } else {
                    expression.append(
                            s[0] + " " + ch[0] + " " + s[1] + " " + ch[1] + " " + s[2] + " " + ch[2] + " " + s[3]);
                }
                break;
            default:
                expression.append(" ");
        }
  • Frame类:GUI界面
        //GUI中确定按钮监听
        ok.addActionListener(e -> {
            if (n.getText().equals("please enter the parameter n") || r.getText().equals("please enter the parameter r")) {
                JOptionPane.showMessageDialog(null, "请输入参数!");
            } else {
                PrintWriter out_exp = null;
                try {
                    out_exp = new PrintWriter("Exercise.txt");
                } catch (FileNotFoundException ex) {
                    ex.printStackTrace();
                }
                PrintWriter out_ans = null;
                try {
                    out_ans = new PrintWriter("Answers.txt");
                } catch (FileNotFoundException ex) {
                    ex.printStackTrace();
                }
                Solve.Build(Integer.parseInt(r.getText()), Integer.parseInt(n.getText()), out_exp, out_ans);
                out_exp.close();
                out_ans.close();
                out_exp.flush();
                String filepath = System.getProperty("user.dir") + "/Exercise.txt";
                File file = new File(filepath);
                try {
                    BufferedReader input = new BufferedReader(new InputStreamReader(
                            new FileInputStream(file)));
                    jTextArea1.read(input, "READING FILE :-)");
                } catch (Exception ee) {
                    ee.printStackTrace();
                }
            }
        });
        //GUI中检查按钮的监听
        check.addActionListener(e1 -> {                                
            PrintWriter Grade = null;
            try {
                Grade = new PrintWriter("Grade.txt");
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
            //将输入的答案写入inputAnswers文件中
            StringTokenizer st = new StringTokenizer(jTextArea2.getText(), "
");
            FileWriter inputAnswers = null;
            try {
                inputAnswers = new FileWriter("inputAnswers.txt");
            } catch (IOException e) {
                e.printStackTrace();
            }
            while (st.hasMoreTokens()) {
                try {
                    inputAnswers.append(st.nextToken() + "
");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            try {
                inputAnswers.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            Scanner in_exp = null;
            try {
                in_exp = new Scanner(Paths.get("Exercise.txt"));
            } catch (IOException e) {
                e.printStackTrace();
            }
            ;
            Scanner in_ans = null;
            try {
                in_ans = new Scanner(Paths.get("inputAnswers.txt"));
            } catch (IOException e) {
                e.printStackTrace();
            }
            Solve.check(in_exp, in_ans, Grade);
            in_exp.close();
            in_ans.close();
            Grade.close();
            //把Grade的内容以消息框显示出来
            String filepath = System.getProperty("user.dir") + "/Grade.txt";
            File file = new File(filepath);
            String s;
            StringBuilder str = new StringBuilder();
            try {
                BufferedReader br = new BufferedReader(new FileReader(file));
                while ((s = br.readLine()) != null) {
                    str.append(s);
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            JOptionPane.showMessageDialog(null, str.toString());
        });
        //GUI中查看答案按钮的监听
        display.addActionListener(e -> {                                        
            //把题目和标准答案整合成一个文件
            PrintWriter exerciseAndAnswer = null;
            try {
                exerciseAndAnswer = new PrintWriter("exerciseAndAnswer.txt");
            } catch (FileNotFoundException ex) {
                ex.printStackTrace();
            }
            String E;
            String A;
            String filepath = System.getProperty("user.dir") + "/Exercise.txt";
            File file = new File(filepath);
            String filepath1 = System.getProperty("user.dir") + "/Answers.txt";
            File file1 = new File(filepath1);
            try {
                BufferedReader inputE = new BufferedReader(new FileReader(file));
                BufferedReader inputA = new BufferedReader(new FileReader(file1));
                while ((E = inputE.readLine()) != null && (A = inputA.readLine()) != null) {
                    StringBuilder s = new StringBuilder();
                    s.append(E + " " + A);
                    exerciseAndAnswer.println(s.toString());
                }
            } catch (Exception ee) {
                ee.printStackTrace();
            }
            exerciseAndAnswer.close();
            //读取
            String filepath2 = System.getProperty("user.dir") + "/exerciseAndAnswer.txt";
            File file2 = new File(filepath2);
            try {
                BufferedReader input = new BufferedReader(new InputStreamReader(
                        new FileInputStream(file2)));
                jTextArea1.read(input, "READING FILE :-)");
            } catch (Exception ee) {
                ee.printStackTrace();
            }
        });
  • Pair类的主要方法:分数运算
public class Pair {
    public Pair add(Pair b) {}// +
    public Pair dec(Pair b) {}// -
    public Pair mul(Pair b) {}// *
    public Pair div(Pair b) {}// /
    public boolean compare(Pair b) {}// >=
}
  • solve类的主要方法:完成题目要求的逻辑部分,判断计算表达式是否合法,写入文件,check答案。
public class Solve {
    // 将随机生成的表达式转化为后缀表达式,用于后续计算
    public static String to_suf(String expression) {}
    // 计算后缀表达式 并返回运算顺序
    public static Pair calculate(String expression, StringBuilder cal) {}
    // check答案
    public static void check() {}
    // 生成n个表达式,范围为r
    public static void Build(int r, int n, PrintWriter out_exp, PrintWriter out_ans) {
        //使用hash_set判重
        HashSet<String> st = new HashSet<String>();
        for (int i = 0; i < n; ++i) {
            StringBuilder cal = new StringBuilder("");
            String expression = Builder.get_expression(r);
            if (expression.equals(" ")) {
                System.out.println("Error");
                return;
            }
            Pair ans = calculate(expression, cal);
            if (ans.isLessZero() || st.contains(cal.toString())) {
                i--;
                continue;
            }
            st.add(cal.toString());
            out_exp.println((i + 1) + "." + expression + " =");
            out_ans.println(ans.toString());
        }
    }
}

PSP

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

总结

  • 郑梓淳
    1.由于之前已经做过wc个人项目,所以这次做起来比较顺手,比如工具的使用等
    2.题目生成中括号的插入是直接手动拼接的,如果运算符的个数是随机的,这种做法显然不行,有取巧的成分
    3.第一次做GUI,只实现功能,界面的布局较简陋
    4.结对的感受:此次的项目交流,除了讨论分工外,其余都是我在问结对伙伴问题,感谢大佬带飞。结对的好处是可以做自己擅长的方面,这样效率会比较高。

  • 张强
    1.刚接触java,很多地方不熟悉,感觉怎么写怎么报错,很麻烦。不过确实比c++方便。
    2.写的時候不够认真,很多地方不够严谨,会出现除0,没化简等情况,都是在后面的测试里找到的问题,改了改又交上git了,提交的次数感觉比较多。
    3.结对的感受:互相监督会比较有意愿去学习,遇到问题也可以相互讨论。GUI啥的伙伴很主动去学习,遇到问题也会尽量找方法解决,后面测试找到很多bug,很认真,牛。

以上是关于结对项目-ExpressionBuilder的主要内容,如果未能解决你的问题,请参考以下文章

结对-结对编程项目作业名称-结对项目总结

个人项目复用代码实现结对编程项目

结对-英文词频分析-结对项目总结

总结如何复用个人项目实现结对编程项目,以及结对编程的经验教训。

结对项目

结对编程项目报告