结对项目

这个作业属于哪个课程 网工1934-软件工程
这个作业要求在哪里 结对项目
这个作业的目标 <学习如何结对做一个软件项目,了解项目各流程,学会如何与搭档合作进行编程、测试、性能分析等来完成一个初步软件,在合作过程中找到更加高效的工作方法,同时继续学习如何写好一个博客,熟悉掌握github的使用。>

搭档

姓名:李晓兰 学号:3219005401
姓名:郭海燕 学号:3219005400

github

结对项目作业github链接

一、项目需求

题目:实现一个自动生成小学四则运算题目的命令行程序

描述如下:

(1)0使用 -n 参数控制生成题目的个数,例如
Myapp.exe -n 10 将生成10个题目。
(2)使用 -r 参数控制题目中数值(自然数、真分数和真分数分母)的范围,例如
Myapp.exe -r 10 将生成10以内(不包括10)的四则运算题目。该参数可以设置为1或其他自然数。该参数必须给定,否则程序报错并给出帮助信息。
(3)生成的题目中计算过程不能产生负数,也就是说算术表达式中如果存在形如e1− e2的子表达式,那么e1≥ e2。
(4)生成的题目中如果存在形如e1÷ e2的子表达式,那么其结果应是真分数。
(5)每道题目中出现的运算符个数不超过3个。
(6)程序一次运行生成的题目不能重复,即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目。例如,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。
(7)在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件,格式如下:
1.答案1
2.答案2
特别的,真分数的运算如下例所示:1/6 + 1/8 = 7/24。
(8)程序应能支持一万道题目的生成。
(9)程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计,输入参数如下:
Myapp.exe -e .txt -a .txt
统计结果输出到文件Grade.txt,格式如下:
Correct: 5 (1, 3, 5, 7, 9)
Wrong: 5 (2, 4, 6, 8, 10)
其中“:”后面的数字5表示对/错的题目的数量,括号内的是对/错题目的编号。为简单起见,假设输入的题目都是按照顺序编号的符合规范的题目。

二、项目开发流程分析

(一)开发环境

1、编程语言:java
2、项目集成工具:Intellij IDEA 2019.3.4
3、项目框架工具:maven
4、单元测试工具:JUnit4
5、性能分析工具:JProfiler 12.0.3.0

(二)实现算法原理

(1)中缀表达式转换为后缀表达式

若将中缀表达式1 + 23 + (4 * 5 + 6) * 7转为后缀表达式,则其转换后的表达式为1 2 3 * + 4 5 * 6 + 7 * +,中间以空格作为间隔。
具体步骤规则如下:
(1) 从头扫描中缀表达式,若为数字,直接输出,不入栈
(2) 如果遇到左括号,其优先级最高,直接入栈,但只有遇到右括号才出栈
(3) 如果遇到“+”“-”符号,其优先级最低,不断将比自己高级的符号弹出栈并输出,直到遇到左括号,左括号不出栈
(4) 如果遇到“
”“÷”符号,只需将同级符号弹出栈并输出,加减号优先级低,左括号又不能弹出,所以遇到这三者都直接入栈
(5) 如果遇到右括号,只需将栈顶依次弹出并输出,直到遇到左括号,将左括号弹出
(6) 最后,把栈中剩余的符号全部弹出栈并输出

(2)后缀表达式的计算

具体步骤概括为:遍历表达式,遇到操作数则将操作数入栈,若遇到符号,则将栈顶的两个数抛栈并进行运算,运算结果再入栈。
实例:1 2 3 * + 4 5 * 6 + 7 * +;
步骤:
(1)将1、2、3分别入栈,遇到“”,则计算12=2,将2入栈;
(2)遇到“+”,则取栈顶2和3,计算2+3=5,将5入栈;再将4、5分别入栈,此时从栈顶往下为5、4、5;
(3)接着遇到“”,取栈顶5、4,计算54=20,将20入栈;
(4)将6入栈,此时从栈顶往下为6、20、5;
(5)接着遇到“+”,取栈顶6、20,计算6+20=26,将26入栈;
(6)将7入栈,此时从栈顶往下为7、26、5;接着遇到“”,取栈顶7、26,计算726=182,将182入栈;
(7)接着遇到“+”,取栈顶182、5,计算182+5=187,将187入栈,则最终计算结果为187。

(三)整体流程图

图片名称

(四)项目框架及主要类

主要maven框架

图片名称

(五)项目模块核心算法

(1)文件读写模块fileIO类

 BufferedReader br1 = new BufferedReader(new FileReader(file1));
 BufferedReader br2 = new BufferedReader(new FileReader(file2));
 int i = 1;
 String str = null;
 while((str = br1.readLine()) != null){
    if(str.equals(br2.readLine())){
         correct.add(Integer.toString(i));//比较两文件,相等则将正确的题目序号加入集合
     }else{                               //错误则将错误的题目序号加入集合
         wrong.add(Integer.toString(i));
     }
     i++;
  }
  //将正确集合的元素写入文件
 this.writeFile(Grade,"Correct:"+correct.size()+"(");
 //将错误集合的元素写入文件
 this.writeFile(Grade,"Wrong:"+wrong.size()+"(");
      

(2)生成中缀表达式equationCreate类

随机生成数字、运算符、括号
 public  String numberCreate(int m){}
 public String symbolCreate(){}
 public  boolean bracket(){}
对分数进行约分化简
 public  String Simplify(int a,int b){
    String s = "";
    int commonNum = 1 ,c;//公约数
    c = a/b;
    a = a % b;
    if(c < 0){
        a = a*(-1);//当c小于0时,有一个负号了,将余数的负号删去
    }
    for(int i =1;i <= a;i++){
        if(a % i==0 && b % i ==0){
            commonNum = i;  //求出最大公约数
        }
    }
    a = a / commonNum;
    b = b / commonNum;   //化简
    if(a == 0){
       s = Integer.toString(c);
    }else if(c == 0){
       s = Integer.toString(a)+"/"+Integer.toString(b);
    }else{
       s = Integer.toString(c)+"'"+Integer.toString(a)+"/"+Integer.toString(b);
    }
    return s;
}

(3)转化为后缀表达式beSuffix类

中缀转后缀
      for (int i = 0; i < length; i++) {
            char e ;
            char ch = equ.charAt(i);
            switch (ch){
                case '(':        //左括号优先级最高,直接入栈,但只有遇到有括号才出栈
                    s.push(ch);
                    break;
                case '+':
                case '-':
                    newStr += " ";
                    while(s.size() != 0){
                        e = s.pop();
                        if(e == '('){  //优先级最低,不断将把自己高级的弹出栈并输出,知道遇到左括号,左括号不出栈
                            s.push('(');//前面弹出栈,现在又压入栈
                            break;
                        }else{
                            newStr += e;
                            newStr += " ";}
                    }
                    s.push(ch);
                    break;
                .........//乘除及右括号情况省略,具体查看源码
        while(s.size() != 0){  //把栈中剩余的符号全部输出
            newStr += " ";
            newStr += s.pop();
        }
        return newStr;
计算后缀表达式
      for (int i = 0; i < strings.length ; i++) {
            if(strings[i].equals("+")||strings[i].equals("-")||strings[i].equals("*")||strings[i].equals("÷")){
                String x2 = stack.pop();
                String x1 = stack.pop();
                String str = myCalculation(x1,x2,strings[i]);
                stack.push(str);//把计算结果压入栈作为新的操作数
                if(str.equals("无解")){
                    return str;
                }
            }else{
                stack.push(strings[i]);//若为数字,直接入栈
            }

(4)查重checkDuplicate类

找出答案相同的
    for (int i = 0; i < answer.size()-1 ; i++) {
            String str = "";
            for (int j = i+1 ; j< answer.size();j++){
                if(answer.get(i).equals(answer.get(j))){
                    if(checkEquation(equation.get(i),equation.get(j))){
                        str += (i+1)+","+equation.get(i)+"repeat"+(j+1)+","+equation.get(j)+" ";
                    }
                }
            }
            if(str.length()>0){
                repeat.add(str);
            }
        }
再比较表达式
    for (int i = 0; i < strings.length ; i++) {
            if(!equ2.contains(strings[i])){
                return false;
    //只查看是否出现此数字或符号,有局限性,但前面答案相同,表达式出现的每个数字或符号都有的概率比较小
            }
        }

(5)主模块mainCreate类

通过手动输入运算式数字的范围以及生成运算式的个数,生成Exercises.txt文件和Answers.txt文件,调用算法,进行查重和答案比对,将测试结果写入Grade.txt文件。

(6)运行结果

图片名称

三、项目性能分析

概览

图片名称

实时内存

图片名称

由上图可知,运行时调用java.lang.String包的次数较多,主要是核心算法的实现,运算式的生成及转换,都是以字符串形式进行,符合性能分析标准。

四、单元测试

部分模块单元测试代码

(一)主模块测试mainTest类

进行10000道运算式的测试,通过静态输入参数,生成Exercises(1).txt文件和Answers(1).txt文件

部分测试代码

        File f1 = new File("Exercises(1).txt");
        File f2 = new File("Answers(1).txt");        
        int n=10000;
        int m = 50;
            for (int i = 0; i < n; i++) {
                String str = equC.equations(n, m);//生成中缀表达式
                String newStr;
                String answer = suffix.calculateSuffix(suffix.becomeSuffix(str));
                //转为后缀表达式并求解
                newStr = i + 1 + ":" + str + "\r\n";//将题目运算式写入文件
                fIO.writeFile(f1, newStr);
                newStr = i + 1 + ":" + answer + "\r\n";//将答案写入文件
                fIO.writeFile(f2, newStr);
            }

测试说明:主要是设置运算式的个数为10000,数字范围为50,进行10000道运算式的测试,生成Exercises(1).txt文件和Answers(1).txt文件。

测试点

图片名称

测试结果

图片名称

(二)读取文件测试filesIOTest类

部分测试代码

@Test
    public void readFileTest(){
        String s = filesIO.readFile("D:\\Test2\\orig.txt");
        String[] strings = s.split(" ");
        for(String str : strings){
            System.out.println(str);
        }//测试路径正常,读取
    }

测试说明:主要是测试文件读取功能,是否能正常读取文件路径,将内容转化为字符串,此处以原始文件路径作为测试样例。

测试点

图片名称

(三)beSuffix测试beSuffixTest类

部分测试代码

@Test
    public void becomeSuffixTest(){
        beSuffix bs = new beSuffix();
        String str = "5+(1*7)";
        String newStr = bs.becomeSuffix(str);
        System.out.println(newStr);
    }

测试说明:这部分主要是测试后缀表达式的生成

@Test
    public void calculateSuffixTest(){
        beSuffix bs = new beSuffix();
        String str = "517*+";
        String end = bs.calculateSuffix(str);
        System.out.println(end);
    }

测试说明:这部分主要是测试计算后缀表达式的结果

测试点

图片名称

覆盖率

图片名称

(四)查重测试类checkDuplicateTest类

部分测试代码

@Test
    public void checkEquationTest(){
        checkDuplicate cd = new checkDuplicate();
        String equ1 = "1+(2+3)";
        String equ2 = "1+2+3";
        boolean tag = cd.checkEquation(equ1,equ2);
        System.out.println(tag);
    }

测试说明:主要是测试两个计算式是否相同,排除括号及交换律、结合律的影响。

测试点

图片名称

测试结果

图片名称

(五)运算式生成测试类equationCreateTest类

部分测试代码

@Test
    public void equationsTest(){
        equationCreate ec = new equationCreate();
        int m = 10;
        int n=10;
        String s1 = ec.equations(n,m);
        String s2 = ec.equations(n,m);
        String s3 = ec.equations(n,m);
        System.out.println(s1);
        System.out.println(s2);
        System.out.println(s3);
    }

测试说明:主要是测试随机生成3个运算式,进行单元模块的测试。

测试点

图片名称

覆盖率

图片名称

五、项目小结

李晓兰:在这次的结对项目中,我有了很多新的收获。我和队友采用的方式是一起进行编程,每个人编写一部分代码,另一个人在旁边进行代码审查,提供思路和可以优化的地方。通过这次的项目,我认为我们的编程能力都有所提高,也学习了一些新的算法。在合作的过程中,我们遇到了很多问题,比如代码中的一些bug,以及在向github提交时因为新分支无法推送等,有一些一个人发现不了的问题,另一个人就可以发现,这样也省去了很多代码审查的时间。当遇到问题时,我们可以一起想办法解决,虽然会有一些分歧,但是同时工作确实比自己一个人摸索的结果要好得多,也可以达到互补的效果。

郭海燕:此次结对的任务中,我体会到了团队合作时多和队友沟通对完成任务有非常大的帮助,使之后的工作对接很顺利。队友非常厉害,我不是很会写代码,所以她负责了比较多代码部分,甚至在我写代码过程中产生了一些问题,队友都会很耐心地帮我解决,自己在开发上还有很多要向队友学习的。在项目提交的时候再一次出现做个人项目时遇到的问题,都是git push失败,原因都是不能在新的分支上提交项目,这使我对此类问题更加了解和学到更多git和github的使用。

六、PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 60 60
Estimate 估计这个任务需要多少时间 50 50
Development 开发 970 1080
Analysis 需求分析 (包括学习新技术) 200 250
Design Spec 生成设计文档 60 65
Design Review 设计复审 30 30
Coding Standard 代码规范 (为目前的开发制定合适的规范) 30 30
Design 具体设计 70 85
Coding 具体编码 280 310
Code Review 代码复审 50 60
Test 测试(自我测试,修改代码,提交修改) 250 250
Reporting 报告 130 135
Test Repor 测试报告 60 60
Size Measurement 计算工作量 10 15
Postmortem & Process Improvement Plan 事后总结, 并提出过程改进计划 60 60
合计 1160 1275
posted on 2021-10-25 23:50  LXL-zqdn  阅读(52)  评论(0编辑  收藏  举报