• 结对项目


    1.1 Github项目地址

    https://github.com/DFRUfO/pair-program

    1.2 项目成员

    孙争,3118005106;邓富荣,3118005090

    2.1 项目需求

    1. 使用 -n 参数控制生成题目的个数,例如

        Myapp.exe -n 10

        将生成10个题目。

    1. 使用 -r 参数控制题目中数值(自然数、真分数和真分数分母)的范围,例如

        Myapp.exe -r 10

        将生成10以内(不包括10)的四则运算题目。该参数可以设置为1或其他自然数。该参数必须给定,否则程序报错并给出帮助信息。

    1. 生成的题目中计算过程不能产生负数,也就是说算术表达式中如果存在形如e1− e2的子表达式,那么e1≥ e2。
    2. 生成的题目中如果存在形如e1÷ e2的子表达式,那么其结果应是真分数。
    3. 每道题目中出现的运算符个数不超过3个。
    4. 程序一次运行生成的题目不能重复,即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目。例如,23 + 45 = 和45 + 23 = 是重复的题目,6 × 8 = 和8 × 6 = 也是重复的题目。3+(2+1)和1+2+3这两个题目是重复的,由于+是左结合的,1+2+3等价于(1+2)+3,也就是3+(1+2),也就是3+(2+1)。但是1+2+3和3+2+1是不重复的两道题,因为1+2+3等价于(1+2)+3,而3+2+1等价于(3+2)+1,它们之间不能通过有限次交换变成同一个题目。生成的题目存入执行程序的当前目录下的Exercises.txt文件,格式如下:
      1. 四则运算题目1
      2. 四则运算题目2   

           .......

    其中真分数在输入输出时采用如下格式,真分数五分之三表示为3/5,真分数二又八分之三表示为2’3/8。

    1. 在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件,格式如下:
      1. 答案1
      2. 答案2

      特别的,真分数的运算如下例所示:1/6 + 1/8 = 7/24。

    1. 程序应能支持一万道题目的生成。
    2. 程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计,输入参数如下:

      Myapp.exe -e <exercisefile>.txt -a <answerfile>.txt

      统计结果输出到文件Grade.txt,格式如下:

      Correct: 5 (1, 3, 5, 7, 9)

      Wrong: 5 (2, 4, 6, 8, 10)

    其中“:”后面的数字5表示对/错的题目的数量,括号内的是对/错题目的编号。为简单起见,假设输入的题目都是按照顺序编号的符合规范的题目。

    2.2 PSP预估时间

    PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
    Planning 计划 60   
    Estimate 估计这个任务需要多少时间 20  
    Development 开发 800   
    Analysis 需求分析(包括学习新技术) 100   
    Design Spec 生成设计文档 30  
    Design Review 设计复审    
    Coding Standard 代码规范 60   
    Design 具体设计 60  
    Coding 具体编码 300  
    Code Review 代码复审 60  
    Test 测试(自我测试、修改代码、提交修改) 60   
    Reporting 报告 30   
    Test Report 测试报告 20  
    Size Measurement 计算工作量 10  
    Postmortem&Process Improvement Plan 事后总结,并提出过程改进计划 20   
    Total 合计 1630   

     

    3. 解题思路

    编程语言的选择:java

    需要用到的知识:IO流、JTable、栈、HashSet

    结构分析

    对于计算中心思想:用后缀表达式运算。

    用逆波兰法;

    中缀表达式转后缀表达式遵循以下原则:

    1.遇到操作数,直接输出;

    2、栈为空的时候遇到符号,入栈

    3、遇到左括号将其入栈。

    4.遇到右括号,执行出栈操作,并将出栈的元素输出,直到弹出栈的是左括号,左括号不输出;

    5.遇到其他运算符’+’‘’’*’’/'时,弹出所有优先级大于或等于该运算符的栈项元素,然后将该运算符入栈;

    6.最终将栈中的元素依次出栈,输出。

    遇到的困难

    IO流写入会覆盖之前的内容导致写入内容与预期不符,变量类型的转换问题,带分数的计算转化问题

    4. 设计实现过程

    根据需求,对每个功能进行细分、简化,找出每个功能的详细需求,把每个功能都实现完成,生成函数,然后考虑封装调用,在主函数中实现调用各个功能函数,完成开发。

             

    5. 代码

    main方法

    public class major {
        public static void main(String[] args){
    
                Choose choose = new Choose();
    
        }

    主要调用函数

    public class Choose extends JFrame{
        public Choose() {
            super("选择题目数量范围");
            this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            this.setSize(400, 300);
            this.setLocation(700, 250);
            this.setLocationRelativeTo(null);
            this.setResizable(false);
            this.setLayout(null);
            JLabel titleNumber = new JLabel("生成题目的数量:");
            JLabel titleRange = new JLabel("题目的范围:");
            Font font = new Font("微软雅黑", Font.BOLD, 15);
            titleNumber.setFont(font);
            titleRange.setFont(font);
            
            JTextField number = new JTextField();
            JTextField range = new JTextField();
            JButton create = new JButton("生成");
            
            titleNumber.setBounds(50, 50, 160, 50);
            number.setBounds(200, 50, 100, 40);
            titleRange.setBounds(50, 120, 150, 50);
            range.setBounds(200, 120, 100, 40);
            create.setBounds(150, 190, 100, 50);
            this.add(titleNumber);
            this.add(number);
            this.add(create);
            this.add(titleRange);
            this.add(range);
            this.setVisible(true);
            
            create.addActionListener(new ActionListener() {
                
                @Override
                public void actionPerformed(ActionEvent e) {
                    // TODO Auto-generated method stub
                    ReadTxt rt = new ReadTxt();
                    int Number = Integer.parseInt(number.getText());
                    int Range = Integer.parseInt(range.getText());
                    try {
                        String[] title = title(Number,Range);
                        double[] rs = rt.readTxt("Answers.txt");
                        JOptionPane.showMessageDialog(null, "成功生成题目!");
                        dispose();
                        Jtable jTable_01 = new Jtable(title,rs);
                        jTable_01.setVisible(true);
                    } catch (IOException e1) {
                        // TODO Auto-generated catch block
                        e1.printStackTrace();
                        JOptionPane.showMessageDialog(null, "生成失败!");
                    }   //答案文件
                    
                }
            });
        }
        
        //生成题目、答案,并写入文件
        public String[] title(int number,int range) throws IOException {
            CalculateImp c = new CalculateImp();
            Create_title c2 = new  Create_title();
            WriteTxt wt = new WriteTxt();
            Vector title1 = new Vector();
            
            String title_path = "Exercises.txt";
            String result_path = "Answers.txt";
            
            HashSet set = new HashSet();
            ServerImp serverImp = new ServerImp();
            for(int i = 0 ; i < number ; i++) {
                String title =  c2.create_title(range,3);
                double result = Double.parseDouble(c.calculate(title.toCharArray()));
                
                if(result < 0.0 && i != 0) {
                        i--;
                        System.out.println(result);
                        System.out.println("出现负数");
                        continue;
                 }
                
                if(!serverImp.removeRepeat(title,set)){ //去除重复的函数,并把结果存储在set中
                    System.out.println("重复了");
                    i--;
                }
                
            }
    
            Iterator iterator = set.iterator();
            int j = 0;
            while (iterator.hasNext()){
                String title3 = (String)iterator.next();
               
                title1.add(title3);
                System.out.println("第" + String.valueOf(j+1) + "道题目:" + title3 + " =");
                wt.WriteTxt("第" + String.valueOf(j+1) + "道题目:" + title3 + " =", title_path);
                wt.WriteTxt("第" + String.valueOf(j+1) + "道题的答案:" + c.calculate(title3.toCharArray()),result_path);
                j++;
            }
            
            String[] title3 = new String[title1.size()];
            for(int i = 0;i<title1.size();i++){
                title3[i] = (String)title1.get(i);
            }
            return title3;
        }
    }

    生成题目函数Creat_title

    public class Create_title {
    
    
        public String create_title(int range, int num2) {//num:提目数 range:范围 num2:题目组成数量
    
            Random random = new Random(range);
            String[] char_arrays = null;
            int num3 = (int) (Math.random() * num2);//分数的个数
            int num4 = num2 - num3;//正数个数
            int charNum = num2 - 1;//符号个数
    
            int bracketL = 0;//(位置
    
            int[] int_arrays = new int[num4 + num3 * 2];
            boolean flag = isBracket();
            if (flag) {
                char_arrays = returnChar(num3, charNum + 2, flag);
    
            } else {
                char_arrays = returnChar(num3, charNum, false);
            }
    
    
            for (int w = 0; w < num2; w++) {
                int_arrays[w] = random.nextInt(range);
            }
            String title = "";
            if (char_arrays[0].equals("(")) {
                for (int i = 0; i < num2; i++) {
                    title += char_arrays[i] + ((int) (Math.random() * range) + "");
                }
                if (char_arrays[num2].equals(")")) {
                    title += ")";
                }
    
            } else {
                for (int i = 0; i <= num2; i++) {
                    title += ((int) (Math.random() * range) + "") + char_arrays[i];
                }
            }
            if (title.charAt(title.length() - 1) == '+' || title.charAt(title.length() - 1) == '-' || title.charAt(title.length() - 1) == '/' || title.charAt(title.length() - 1) == '*') {
                title = title.substring(0, title.length() - 1);
            }
    
    
            return title;
        }
    
        boolean isBracket() {
            return true;
        }
    
    
    
    
        String[] returnChar(int mark, int charNum, boolean flag) {
            Random random = new Random();
            String[] char_array = new String[charNum];
    
            if (flag) {
                int bracketL = random.nextInt(charNum - 2);//(位置,最后一位和倒数第二位不能是(
    
                int j = charNum - bracketL - 2;
    
                int bracketR = bracketL + random.nextInt(j) + 2;//)位置这个随机数可以生成0
    
                if (bracketL != 0) {
                    char_array[bracketL] = "+(";
                } else {
                    char_array[bracketL] = "(";
    
                }
    
                if (bracketR == charNum - 1) {
                    char_array[bracketR] = ")";
                } else {
                    char_array[bracketR] = ")-";
                }
            }
            int mark1 = 0;
            while (true) {
                if (mark1 == mark) {
                    break;
                }
                int i = (int) (Math.random() * charNum);
                if (char_array[i] == null) {
                    char_array[i] = "/";
                    mark1++;
                }
    
            }
            int num = 0;
            while (true) {
    
                if (num == charNum) break;
                if (char_array[num] == null) {
                    int i = (int) (Math.random()*4);
                    switch (i) {
                        case 0:
                            char_array[num] = "*";
                            break;
                        case 1:
                            char_array[num] = "+";
                            break;
                        case 2:
                            char_array[num] = "-";
                            break;
    
                        case 3:
                            char_array[num] = "÷";
                            break;
    
                    }
                }
    
                num++;
    
            }
            return char_array;
        }
    }

    计算结果函数CalculateImp

    public class CalculateImp {
    
        public String calculate(char[] str) {
            int j = 0;
    
            //中缀表达式转后缀表达式
            double[] num = new double[100];
            char[] operator = new char[100];
            int index = 0;
    
            char[] stack = new char[100];
            double[] stackj = new double[100];
            int top = -1;
    
            int temp = 0; //正在拼写数字
            int flag = 0;//表示是否执行数字
    
            int flag2 = 0;//判断什么时候读完了
    
            while (true) {
    
                if (j == str.length) {
                    flag2 = 1;
                }
                if (flag2 == 0 && str[j] >= '0' && str[j] <= '9') {//读到数字
                    flag = 1;
                    temp *= 10;
                    temp += str[j] - '0';
    
    
                } else {//读到了符号,或者结束
    
                    if (flag == 1) {//如果在拼写一个数字就将一个数字输出
    
                        num[index] = temp;
    
                        operator[index] = '?';
    
                        index++;
                        flag = temp = 0;
                    }
    
                    if (flag2 == 1) {//如果字符串已经结束
    
    
                        while (top >= 0) {
                            int q = top--;
                            if (stack[q] != '(') {
                                operator[index++] = stack[q];
                                
                            }
    
                        }
                        break;
    
                    } else {//读到一个符号
    
                        if (top == -1 || str[j] == '(') {
                            stack[++top] = str[j];
    
    
                        } else if (str[j] == ')') {//如果是右括号则一直出栈道左括号
    
                            while (top >= 0 && stack[top] != '(') {
                                operator[index++] = stack[top--];
    
                            }
    
    
                        } else if (str[j] == '*' || str[j] == '/') {//如果是乘除,则乘除出栈,新符号入栈
                            while ((top > 0) && (stack[top] == '*' || stack[top] == '/')) {
                                operator[index++] = stack[top--];
    
                            }
                            stack[++top] = str[j];
    
                        } else {//如果是加减,则四则运算出栈,新符号入栈
    
                            while ((top >= 0) && (stack[top] !='(')) {//>=0是为了确保第0位可以出栈
                                operator[index++] = stack[top--];
    
                            }
    
                            stack[++top] = str[j];
    
                        }
    
                    }
    
                }
                j++;
            }
    
            top = -1;
            double temp1 = 0;
            double temp2 = 0;
    
            for (int i = 0; i < index; i++) {
                if (operator[i] == '?') {
                    stackj[++top] = num[i];
                } else {
                    temp1 = stackj[top--];
    
                    temp2 = stackj[top--];
    
                    switch (operator[i]) {
                        case '+':
                            stackj[++top] = temp2 + temp1;
                            break;
                        case '-':
                            stackj[++top] = temp2 - temp1;
                            break;
                        case '*':
                            stackj[++top] = temp2 * temp1;
                            break;
                        case '÷':
                            stackj[++top] = temp2 / temp1;
                            break;
                        case '/':
                            stackj[++top] = temp2 / temp1;
                            break;
                    }
    
                }
            }
            double result = stackj[0];
            return result + "";
        }
    }

    去重函数ServerImp

    public class ServerImp {  //去重
        boolean flag = true;
        public boolean removeRepeat(String title, HashSet set) {
            if(title.length()>5){
                set.add(title);
            }
            if (title.charAt(0) == '(') {
                if (title.length() == 5) {//只有出现两个数字相加的时候才允许运行
                    add(title, set, true);
                    System.out.println("didid");
                }
    
            } else if (title.length() == 3) {
                add(title, set, false);
                System.out.println("lal");
            }
    
    
            return  flag;
        }
    
        void add(String title, HashSet set, boolean flag) {//用于两个数相加的情况
            String a = null;
            String b = null;
            String c = null;
            if (flag) {
                a = String.valueOf(title.charAt(1));
                c = String.valueOf(title.charAt(2));
                b = String.valueOf(title.charAt(3));
            } else {
                a = String.valueOf(title.charAt(0));
                c = String.valueOf(title.charAt(1));
                b = String.valueOf(title.charAt(2));
            }
    
            String temp = b + c + a;
            if (set.add(temp)) {
                set.remove(temp);
                set.add(a + c + b);
                this.flag = true;
            } else {
                this.flag = false;
                System.out.println("出现重复");
            }
        }
    }

    6. 测试运行

    首先生成20道题目

                 

              

    提交答案后生成答题情况

    写入的文件

              

    生成10000道题目

    7. 实现完程序后,实际花费时间

    PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
    Planning 计划   60 
    Estimate 估计这个任务需要多少时间   20 
    Development 开发   1200 
    Analysis 需求分析(包括学习新技术)    150
    Design Spec 生成设计文档    30
    Design Review 设计复审    
    Coding Standard 代码规范    60
    Design 具体设计    60
    Coding 具体编码    300
    Code Review 代码复审    60
    Test 测试(自我测试、修改代码、提交修改)    60
    Reporting 报告    20
    Test Report 测试报告    20
    Size Measurement 计算工作量    10
    Postmortem&Process Improvement Plan 事后总结,并提出过程改进计划    20
    Total 合计    2070

    8. 项目小结

         这次项目是结对项目,不能自己干自己的了,不能想怎么干就怎么干,要考虑队友的想法、时间和能力,由于这次疫情的关系,我们不能面对面地交流,只能线上讨论,额外增加了讨论的时间,有时候找个Bug都要好久,但这些并不妨碍我们完成此次项目。

         结对项目比上次的个人项目较难,所以分工很重要,开始时我们就讨论好各自负责的工作,交换各自的想法,在实际开发遇到的问题一起解决,这次项目使我们认识到,在团队项目开发中,分工合作、代码规范很重要,沟通交流更重要。这次项目也暴露了我们很多缺点,做事拖拉,编码不规范,细节不够重视,测试能力欠缺,debug速度不够,这些都是需要我们在日后的实际开发中一点一点地纠正的,我们也会不断学习,不断进步。

     

  • 相关阅读:
    Find a way(两个BFS)
    ACM代码模板
    ElasticSearch-集群
    ElasticSearch-倒排索引
    ElasticSearch-IK分词器
    ElasticSearch-数据类型
    ElasticSearch-REST APIS
    cmd命令行中的errorlevel和延迟赋值
    ubuntu 12.04内核升级到3.13.1
    ubuntu 12.04安装TP-LINK TL-WN725N v2
  • 原文地址:https://www.cnblogs.com/frspring/p/12609824.html
Copyright © 2020-2023  润新知