个人开发流程----计应191第一小组赵会丽

 

个人开发流程(psp)

 

psp数据比较  预计vs实际
PSP阶段 预计所花时间百分比 实际所花时间百分比
计划  8  16
  • 明确需求和其他相关因素,估计每个阶段的时间成本
 8 1 6
开发 160 200
  • 需求分析
 6  10
  • 生成设计文档
 5  6
  • 设计复审(和同事审核设计文档)
 4  6
  • 代码规范(为目前的开发制定合适的规范)
 3  3
  • 具体设计
 10  12
  • 具体编码
 36 21 
  • 代码复审
 7 9
  • 测试(自测,修改代码,提交修改)
 13  21
报告  9  6
  • 测试报告
 3  2
  • 计算工作量
 2  1
事后总结,并提出过程改进计划  3  3

一、需求分析:编写一个四则运算程序,具体操作如下:

从整个需求来看,大致可以分为三个模块,生成表达式,表达式计算(包括将表达式和答案写入文件),答题报告的生成(包括表达式的查重和根据输入完成的校验)。各个模块之间关系密切,故采用由下至上的不断迭代的开发模式,先开发最底层的各个桩模块,在开发每个桩模块时再采用测试驱动开发的方法,从编写小的测试用例开始开发桩模块,当对桩模块进行整合时,再编写测试模块,这样当开发完成后测试也就完成了。
  生成表达式模块:主要的关键点在于如何能够全覆盖所有不超过三个运算符含有真,假分数,整数的可以带有括号的四则运算表达式。在忽略优先级的条件下,我们观察表达式形如(1+2+3)*6,1+2+3, 1+2,的三个表达式,我们会发现任何一个复杂的表达式均可看作,其少一个运算符的表达式和一个运算符以及一个运算数构成,如上的例子中
(1+2+3)*6 可以看作是由(1+2+3)和*以及6构成,而1+2+3 则可以看做是由1+2 和+以及3构成。这就给了一个思路只要编写一个能随机生成所有情况下的带一个运算符的表达式,就可以通过不断添加运算符和运算操作数的方式而穷尽所有的可能。
  表达式计算:可采用将随机生成的表达式转换为后缀,在对后缀表达式进行计算完成。
  答题报告的生成:关键点在于如何实现查重,首先我们要明白何为重复的表达式?根据题目中的定义“程序一次运行生成的题目不能重复,即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目。例如,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,它们之间不能通过有限次交换变成同一个题目。" 仔细观察题目中所给的两个例子,你会发现这里所说的重复的意思就是你不能破坏原本计算时的优先级,在这个基础上你可以交换加法或者乘法的。解析一下题中的例子,比如3+(2+1)和1+2+3这两个题目,为什么是重复的呢?按照优先级,我们是如何计算左边题目的,是先计算2+1的这个加法再计算3加上2+1的结果的这个加法的对吧,而右边这个题目根据加法的左结合,先计算1+2 的这个加法 再计算3加上1+2 这个结果的加法,运算时的符号的优先级并没有被改变仅仅改变了加法的左右操作数,满足重复的定义,所以是重复的。
  再看一个不重复的例子,1+2+3和3+2+1,同样的左边的题目,先计算的是1+2的这个加法再计算3加上1+2的结果的这个加法,而右边的则是先计算3+2的这个加法再计算1加上3+2的结果的加法.可以理解为在进行运算时整个运算的流程是不同的,所以是不同的表达式.
通过上述的分析我们可以看到如何判定两个表达式是否重复关键在于如果在计算这两个表达式时的流程完全一致或者仅在加法或者乘法运算时交换了左右操作数则这样的两个运算表达式是重复的,这样的分析很容易让我们相到用后缀表达式解决查重,但仅仅依靠后缀表达式是不够的,后缀表达式是能够体现计算过程但却无法用于判断两个表达式之间计算过程是否相同的直接体现,这是·两个重复的表达式很可能会有不同后缀表达式。比如以(1+2)*3+5和3*(1+2) +5 为例,(1+2)*3+5的后缀表达式为1 2 + 3 * 5 + 而3*(1+2) +5的后缀表达式为3 1 2 + * 5+,虽然是相同运算过程的体现但表达式之间的差距却非常的大,那该如何解决?
  所谓相同的运算过程就是,你在做什么运算我也做什么运算,你的操作数是啥我的操作数也是啥,出于这个引入一个新的表达式,我将其命名为查重表达式,他的结构是运算在先后面跟着这个运算符的操作数,以上述为例(1+2)*3+5的查重表达式就为: +12 * 3 +5 这个表达式的含义就是第一个运算的是加法,加法的操作数是2 和3 第二个运算是乘法,乘法的操作数是前一步计算的结果和3以此类推,而3*(1+2) +5的查重表达式为:+12 * 3+5 与上述完全一致!这样在做查重是只需判断查重表达式是否一致或者在查重表达式中第一个字符为‘+’ 或者‘*’的情况下后续的两个操作数互换位置后是否一致即可。也就是说上述的查重表达式于+21 * 3+5 也是等价的。
那该如何生成查重表达式,所谓的查重表达式就是描述表达式正在运算的过程,那就在你使用后缀表达式计算时,去生成即可。算法之后给出
二、功能设计:
功能设计如图:

 

三、代码实现

1、表达式类代码主要用于存储表达式的代码,如下: 

package com.zhl.expression;

public class Expression {
    private String operatorNumber[];// 表示操作数,该字符串数组至存储操作数
    private String[] operator;// 表示操作符,该字符串数组只存储操作符,特别是第一个位置表示该表达式是否为操作符开头
    private String answer;// 表示为结果
    private String cnkiExpression[];// 用于查重
    private int cnkflag = 0;// 用于标识该表达式否重复

    public Expression() {
        operatorNumber = new String[5];// 为了方便将所有数组初始化为空
        for (int i = 0; i < operatorNumber.length; i++) {
            operatorNumber[i] = null;

        }
        operator = new String[6];// 为了方便将所有数组初始化为空
        for (int i = 0; i < operator.length; i++) {
            operator[i] = null;

        }
        cnkiExpression = new String[7];// 为了方便将所有数组初始化为空
        for (int i = 0; i < cnkiExpression.length; i++) {
            cnkiExpression[i] = null;

        }
    }

    public String[] getOperatorNumber() {
        return operatorNumber;
    }

    public void setOperatorNumber(String[] operatorNumber) {
        this.operatorNumber = operatorNumber;
    }

    public String[] getOperator() {
        return operator;
    }

    public void setOperator(String[] operator) {
        this.operator = operator;
    }

    public String getAnswer() {
        return answer;
    }

    public void setAnswer(String answer) {
        this.answer = answer;
    }

    public String[] getCnkiExpression() {
        return cnkiExpression;
    }

    public void setCnkiExpression(String[] cnkiExpression) {
        this.cnkiExpression = cnkiExpression;
    }

    public int getCnkflag() {
        return cnkflag;
    }

    public void setCnkflag(int cnkflag) {
        this.cnkflag = cnkflag;
    }
    /**
     *
     * @return 以字符串的形式发挥表达式
     */
    public String getExpression() {
        String expression ="";
        int i=0,k=0;
        if(operator[k] !=null) expression +=operator[k];
        k++;
        for(;operatorNumber[i] !=null;) {
            expression +=operatorNumber[i++];
            if(operator[k] !=null) {
                expression+=operator[k];
                k++;
            }else {
                break;
            }
        }
        if(operatorNumber[i] !=null) expression +=operatorNumber[i];
        if(operator[k] !=null) expression +=operator[k];
        return expression;

    }
    /**
     *
     * @return 以字符数组的形式发挥表达式
     */
    public String[]toStringArray () {
        String[] result =new String[9];
        int i=0,k=0,j=0;
        if(operator[j] !=null) result[j++]=operator[k];
        k++;
        for(;operatorNumber[i] !=null;) {
            result[j++] +=operatorNumber[i++];
            if(operator[k] !=null) {
                if(operator[k].length()>1) {
                result[j++]=operator[k].substring(0,1);
                result[j++]=operator[k].substring(1,2);
            }else {
                result[j++]=operator[k];
            }
                k++;
            }else {
                break;
            }
        }
        if(operatorNumber[i] !=null) result[j++]=operatorNumber[i];
        if(operator[j] !=null) result[j++] +=operator[k];
        return result;

    }
}

 2.用于随机产生一个表达式的顶层类RandExpressionExport中产生随机表达式getExpression 方法代码如下:

/**
 * 对RandomExprotMachine和RandExpressionExport类的进一步封装
 * @author zhl092911
 *
 */
public class RandExpressionExport {
    private RandOperatorNumberExport rom = new RandOperatorNumberExport();//用于产生任何一个随机数,包括真、家、整数
    private RandomExprotMachine rem = new RandomExprotMachine();//用于产生一个随机数字和一个随机操作字符。
    private Ruler ruler = new Ruler();//描述产生的规则
    /**
     * 生成表达式
     * @param range 代表生成操作数的范围
     * @return 返回表达式
     * 此处有以下的假设情景
     * 1、假设出现三操作符的概率与出现两操作符的概率一致
     * 2.若出现两操作符则无需出现括号,原因是因为个人认为这样的运算意义不大比如(1+2)??显然并没有必要生成
     * 3、不产生毫无意义的表达式,比如+1+2+3之类的表达式
     * 4、假设括号出现在有意义的地方的概率为1/2
     * 5、假设不出现无效的括号嵌套比如((1+2))*3类似这种
     */

    public Expression getExpression(int range) {
        int flag = (int) (Math.random() * 3);
        if (flag == 0) {// 一操作的情况
            return getOneOperatorExpression(range, 0, 1);
        } else if (flag == 0) {// 两操作的情况
            flag = (int) (Math.random() * 3);
            if (flag == 0) {// 两操作符不带括号
                return getTwoOpressionExpression(range, 0, 1);
            } else if (flag == 1) {//两操作符带做左括号
                return getTwoOpressionExpressionWithLeftBrackets(range, 0, 1);
        } else {
                flag = (int) (Math.random() * 3);
                if (flag == 0) {//三操作符不带括号
                    return getThreeOperatorExpression(range, 0, 1);
                } else if (flag == 1) {//三操作符带做左括号
                    return getThreeOperatorExpressionWithLeftBrackets(range, 0, 1);
                }else {//三操作符带做右括号
                    return getThreeOperatorExpressionWithRightBrackets(range, 0, 1);
                }
            }
        }
        return null;
    }
}
 }

3.用于随机产生一个随机一个表达式的顶层类RandExpressionExport 中随机产生一个操作符表达式的getOperatorExpression方法的代码如下:

/**
         * 该方法用于生成一个操作符的表达式
         *param range代表生成操作数的范围
         *param OperNumBeginPosion代表生成的操作数在数组中的位置
         *param OperatorPosition 代表生成的操作符在数组中的位需
         *@return
         */
        public Expression getOneOperatorExpression(int range, int OperNumBeginPosion, int OperatorPosition) {
        Expression expression = new Expression(
        String operator = rem.getRandOperatorString ();
        String leftOperatorNumber = rom.getRandOperatorNumber (range);
        String rightOperatorNumber = ruler.getRightOperatorNumber(operator,rom. getRandOperatorNumber(range), range);
        expression.getOperatorNumber () [OperNumBeginPosion] = leftOperatorNumber;
        expression.getOperatorNumber ()[OperNumBeginPosion+1] =rightOperatorNumber;
        expression.getOperator()[OperatorPosition] =operator;
        return expression;
        }
        /**
        * 该方法用于生成两个操作符的表达式*param range代表生成操作数的范围
        * @param OperNumBeginPosion代表生成的操作数在数组中的位置
        *param OperatorPosition代表生成的操作符在数组中的位置
        *return
        */
        public Expression getTwoOperatorExpression(int range, int OperNumBeginPosion, int OperatorPosition){
        Expression expression = getOneOperatorExpression(range,OperNumBeginPosion,OperatorPosition);
        String operator = rem.getRandOperatorString ();//生成一个随机的操作符
        String thridOperatorNumber = ruler.getRightOperatorNumber (operator,rom.getRandOperatorNumber (range),range); //生成一个符合要求拍脯初的操
        expression.getOperatorNumber () [OperNumBeginPosion+2] = thridOperatorNumber;
        expression.getOperator()[OperatorPosition+1]= operator;
        return expression;
}

4.用于随机产生一个表达式的顶层类RandExpressionExport中随机产生带有两个操作符的表达式的getTwoExpression方法的代码如下:

/**
 2          * 该方法用于生成一个操作符的表达式
 3          *param range代表生成操作数的范围
 4          *param OperNumBeginPosion代表生成的操作数在数组中的位置
 5          *param OperatorPosition 代表生成的操作符在数组中的位需
 6          *@return
 7          */
 8         public Expression getOneOperatorExpression(int range, int OperNumBeginPosion, int OperatorPosition) {
 9         Expression expression = new Expression(
10         String operator = rem.getRandOperatorString ();
11         String leftOperatorNumber = rom.getRandOperatorNumber (range);
12         String rightOperatorNumber = ruler.getRightOperatorNumber(operator,rom. getRandOperatorNumber(range), range);
13         expression.getOperatorNumber () [OperNumBeginPosion] = leftOperatorNumber;
14         expression.getOperatorNumber ()[OperNumBeginPosion+1] =rightOperatorNumber;
15         expression.getOperator()[OperatorPosition] =operator;
16         return expression;
17         }
18         /**
19         * 该方法用于生成两个操作符的表达式*param range代表生成操作数的范围
20         * @param OperNumBeginPosion代表生成的操作数在数组中的位置
21         *param OperatorPosition代表生成的操作符在数组中的位置
22         *return
23         */
24         public Expression getTwoOperatorExpression(int range, int OperNumBeginPosion, int OperatorPosition){
25         Expression expression = getOneOperatorExpression(range,OperNumBeginPosion,OperatorPosition);
26         String operator = rem.getRandOperatorString ();//生成一个随机的操作符
27         String thridOperatorNumber = ruler.getRightOperatorNumber (operator,rom.getRandOperatorNumber (range),range); //生成一个符合要求拍脯初的操
28         expression.getOperatorNumber () [OperNumBeginPosion+2] = thridOperatorNumber;
29         expression.getOperator()[OperatorPosition+1]= operator;
30         return expression;
31 }

 

5、用于将表达式转换成后缀表达式的PostfixExpression类中的getPostFixExpression代码如下:

 1 /**
 2  * 完成将表达式对象转换为String 数组输出
 3  * @author zhl092911
 4  *
 5  */
 6 public class PostfixExpression {
 7 private Ruler ruler=new Ruler();
 8 public String[] getPostFixExpression(Expression expression) {
 9     Stack<String> stack = new Stack<String>();
10     int k=0;
11     String postFixExpression[] = new String [9];
12     String midFixExpression[] = expression.toStringArray(); 
13     int length =ruler.getTrueLength(midFixExpression);
14     for(int i=0;i<0;i++) {
15         String temp;
16         String str = midFixExpression[i];
17         switch (str){
18         case "(": 
19             stack.push(str);
20             break;
21         case "+":
22         case "-":
23             while (stack.size() !=0) {
24                 temp = stack.pop();
25                 if(temp.equals("(")) {
26                     stack.push("(");
27                     break;
28                 }
29                 postFixExpression[k++] =temp;
30             }
31         stack.push(str);
32         break;
33         case "*":
34         case "÷":
35             while(stack.size() !=0){
36                 temp =stack.pop();
37                 if (temp.equals("(") || temp.equals("+") || temp.equals("-")){
38                     stack.push(temp);
39                     break;
40                 }else { 
41                     postFixExpression[k++] =temp;
42                     }
43             }
44             stack.push(str);
45                 break;
46         case ")":
47             while(stack.size() !=0){
48                 temp =stack.pop();
49                 if (temp.equals("("))
50                     break;
51                 else 
52                     postFixExpression[k++] =temp;
53                 }
54             break;
55         default:
56             if(!str.contains("'") && !str.contains("/")) {
57                 str +="1";
58             }
59             postFixExpression[k++] =str;
60         }
61     }
62     while(stack.size() !=0){
63         String temp = stack.pop();
64         postFixExpression[k++] =temp;
65     }
66     return postFixExpression;
67 }
68 }

 

6、用于计算后缀表达式的CalculateExpression类中的calculatePostFixExpression方法代码如下:

1 public class CalculateExpression {
 2     /**
 3      * param targetArray后缀表达式
 4      * @return计算结果
 5 */
 6 public String calculatePostFixExpression (String[] targetArray){
 7     Stack<String> stack = new Stack<String> ();
 8     Ruler ruler;
 9     int lenth = ruler.getTrueLength (targetArray);//获得后缀表达式的字符串数组的真实长度
10     String tempstr = null;
11     for (int i = 0; i< lenth; i++){//字符串的长度超过一,代表这是数字则压栈
12         if (targetArray[i].length () >1) {
13         stack. push (targetArray[i]);
14         }else {//代表这是运算符
15         String leftOperatorNumber = changeTofalseFractionNumber (stack.pop());//将操作数转为假分数
16         String rightOperatorNumber = changeTofalseFractionNumber(stack.pop());//将操作数转为假分数
17          String tempReuslt = calculate(leftOperatorNumber,rightOperatorNumber,targetArray[i]);
18         if(tempReuslt==null)return null;
19         stack.push (tempReuslt);
20 }
21     String resultString = stack.pop ();//返回化简的结果
22     return CalculateRuler.simplificationEracrion(Integer.parseInt(CalculateRuler.getNumerator(resultString)),Integer.parseInt(CalculateRuler.getDeno);
23 
24 }

7、用于描述计算规则的CalculateRuler中的用于化简表达式的方法simplicationFracrion的代码如下:

 1 public class CalculateExpression {
 2     /**
 3      * param targetArray后缀表达式
 4      * @return计算结果
 5 */
 6 public String calculatePostFixExpression (String[] targetArray){
 7     Stack<String> stack = new Stack<String> ();
 8     Ruler ruler;
 9     int lenth = ruler.getTrueLength (targetArray);//获得后缀表达式的字符串数组的真实长度
10     String tempstr = null;
11     for (int i = 0; i< lenth; i++){//字符串的长度超过一,代表这是数字则压栈
12         if (targetArray[i].length () >1) {
13         stack. push (targetArray[i]);
14         }else {//代表这是运算符
15         String leftOperatorNumber = changeTofalseFractionNumber (stack.pop());//将操作数转为假分数
16         String rightOperatorNumber = changeTofalseFractionNumber(stack.pop());//将操作数转为假分数
17          String tempReuslt = calculate(leftOperatorNumber,rightOperatorNumber,targetArray[i]);
18         if(tempReuslt==null)return null;
19         stack.push (tempReuslt);
20 }
21     String resultString = stack.pop ();//返回化简的结果
22     return CalculateRuler.simplificationEracrion(Integer.parseInt(CalculateRuler.getNumerator(resultString)),Integer.parseInt(CalculateRuler.getDeno);
23 
24 }
25 
26     

8、用于实现查重的Cnki类中的得到查重表达式的getCnkiExpressionArray的方法代码如下:

 1 public class Cnki {
 2 /**
 3 *param expression 传入表达式
 4 *@param targetArray 由于后缀表达式在计算后缀表达式时会用到,这里直接传入后缀表达式减少再次用表达式求后缀的重复.
 5 *完成从后缀表达式构造查重表达式的过程(主要是运用堆栈的做法,基本和计算后缀表达式一样的做法).
 6 ★算法描述大致如下:例如原式为:3*(1+2)+5其后缀为12+3*5+
 7 *以后缀的长度循环,遇到数字压栈,遇到字符连续出栈两个数字字符,出栈后将="压入数字栈,这个字符只起占位的作用
 8 *方便代码编写.循环结束后即可得到查重表达式
 9 *
10 */
11 
12     public void getCnkiExpressionArray(String[] targetArray, Expression expression){
13         String topstr=null;//楼顶的字符串
14         int cnkiArrayLength= 0;//用于表示查重表达式的长度
15         Stack<String> stack = new Stack<String>();
16         int lenth = ruler.getTrueLength (targetArray);//获得后缀表达式的真实长度
17         String tempStr = null;
18         for (int i = 0; i< lenth; i++){//字符串的长度超过一,代表这是数字则压栈
19             if (targetArray[i].length() > 1 || targetArray[i].equals ("#")) {
20                 stack.push (targetArray[i]);
21             }else {//代表这是运算符
22                 expression.getCnkiExpression () [cnkiArrayLength++] = targetArray[i];//加入到查重表达式中topstr = stack.pop ;
23             }
24             if( !topstr.equals("#")) {
25                 expression.getCnkiExpression ()[cnkiArrayLength++] = topstr;//加入到查重表达式中}
26             }
27             topstr =stack.pop();
28                 if( !topstr.equals("#")){
29                     expression.getCnkiExpression () [cnkiArrayLength++] = topstr;//加入到查重表达式中I}
30                 }
31                 stack.push ("#");
32 }
33     }

9、用于查重的Cnki类中的判断两个表达式是否重复的代码如下:

 1 /**
 2  * @param exp1要查重的表达式一
 3  * *Cparam exp2要查重的表达式二
 4  * *return这两表达式是否重复
 5  * */
 6 public Boolean cnkiExression (Expression exp1,Expression exp2){
 7     String [] cnkiStrArray1= exp1.getCnkiExpression();
 8     String [] cnkiStrArray2= exp2.getCnkiExpression();
 9     int arrayLenth1 = ruler.getTrueLength(cnkiStrArray1);//获得表达式一查重数组的真实长度
10     int arrayLenth2 = ruler.getTrueLength(cnkiStrArray2);//获得表达式二查重数组的真实长度;
11     if(arrayLenth1!=arrayLenth2) return false;//若数组长度不相等,则表达式一定不同.
12     if(ruler.arrayToString (cnkiStrArray1).equals (ruler.arrayToString (cnkiStrArray2))) return true;//如果查重数组完全一致则是为重复数组.
13     if(cnkiStrArray1[0].equals("+")||cnkiStrArray1[0].equals("*"){//只有加或乘的情况才可能出现.交换左右操作数当做重复的表达式"
14         String temp = cnkiStrArray1[1];//交换首个符号之后的两个数字
15         cnkiStrArray1[1]=cnkiStrArray1[2];
16         cnkiStrArray1[2]= temp;
17     }
18 //若交换后相等则也为重复.
19     if(ruler.arrayToString(cnkiStrArray1).equals (ruler.arrayToString(cnkiStrArray2))) return true;//如果查重数组完全一致则是为重复数组.
20     return false;
21 }

10、用于生成报告的AnswerResport中生成答题报告getAnswerResport方法到你吗如下:

/**
 2  * 该类用作生成答题报告,包括两部分,第一部分为答案校验的报告 第二部分为查重结果的报告
 3  */
 4 public class AnswerResport {
 5     private static Cnki cnki = new Cnki();// 用于查重校验
 6 
 7     /**
 8      * @param replyAnswer用户的回答 *@param rightAnswer 正确的答案 *@return答案的答题报告
 9      */
10     public static String getAnswerResport(String[] replyAnswer, String[] rightAnswer) {
11         String rightAnswerResport = "Correct: ", rightAnswerList = "正确的题号";// 构造正确部分呢过的答题报告
12         String wrongAnswerResport = "Wrong", wrongAnswerList = "错误的题号";// 构造错误部分呢过的答题报告
13         int rightNumber = 0, wrongNumber = 0;// 记录正确和错误的个数
14         for (int i = 0; i < replyAnswer.length; i++) {// 开始校验用户的回答
15             if (replyAnswer[i].equals(rightAnswer[i])) {// 答对
16                 rightNumber++;
17                 rightAnswerList = rightAnswerList + (i + 1) + ", ";
18             } else {// 答错
19                 wrongNumber++;
20                 wrongAnswerList = wrongAnswerList + (i + 1) + ",";
21             }
22         }
23 
24         rightAnswerResport = rightAnswerResport + rightNumber + "-------" + rightNumber + "道题正确," + rightAnswerList+"\n";
25         wrongAnswerResport = wrongAnswerResport + wrongNumber + "------" + wrongNumber + "道题正确, " + wrongAnswerList+"\n";
26         return rightAnswerResport + wrongAnswerResport;
27     }

11、用于生成报告的AnswerResport中生成查重报告cnkiExpressionAnswerReport方法代码如下:

    /**
 2      * @param expregsion 要查重的表达式数组
 3      * return查重报告
 4      */
 5     
 6     public static String cnkiExpressionAngwerReport (Expression [] expression) {
 7         String cnkiReport = "Repeat:"; //返回的表达式报告
 8         String temp = "";
 9         String tempReport = ""; //临时变量,用于出现一次重复情形所产生的信息.
10         int repeatNumber = 0; //计数出现重复表达式的次数。
11         int findRepeatFlag = 0; //标志是否找到重复的表达式
12         for(int i= 0;i<expression.length-1;i++){ //双重循环验证表达式
13             if (expression[i].getCnkflag()==1) continue; //若是已经被标记为重复的则跳过继续下一个表达式
14             for(int j = 1;j<expression.length;j++) {
15                 if (expression[j].getCnkflag()==1) continue;//若 是已经被标记为重复的则跳过继续下一个表达式
16                 if (cnki.cnkiExpression(expression[i], expression[j])){ //出现重 复的表达式
17                     expression [i].setCnkflag(1); //设置重复标志
18                     expression[j].setCnkflag(1); //设置重复标志
19                     findRepeatFlag = 1;
20                     temp = temp + (j+1) +","+expression [j]. getExpression()+" ";
21                 }
22                 if(findRepeatFlag == 1) {//找到重复的
23                     repeatNumber++;
24                     findRepeatFlag = 0;//重置标志
25                     tempReport = "("+ (repeatNumber)+")"+ (i+1)+", "+expression[i]. getExpression() +"Repeat " +tempReport +temp;//构造单次重复报告
26                             temp = "";
27                 }
28             }
29         }
30         cnkiReport= cnkiReport+repeatNumber;
31         if (repeatNumber!=0) {
32             cnkiReport = cnkiReport+"\n"+"RepeatDetail: \n"+tempReport;
33         }
34         return cnkiReport;
35 }
36     }

 

四、小结

1、其实个人认为查重的没什么,实际应用的必要,无论查重用什么实现,不管是用二叉树还是本文所采用的的后缀转换查重表达式,时间复杂度,均在n平方左右,更不用说用于存储二叉树或者查重表达式的所消耗的存储空间里,并且,假设是生成10表达式,且运算操作数的范围为10,生成两个重复的表达式的概率在接近千分之一,几乎是不可能的。

2、测试驱动开发得默认很好用

3、在需求分析和设计上应该投入更多的时间,前期设计并没有考虑查重应用导致后期若想采用二叉树代价略高,只能采用查重表达式的方式,其实现在想想比较好的处理方法应该是采用二叉树的数据结构方式去生成表达式,去计算表达式,去查重表达式,若只在查重阶段采用二叉树,则戴杰太高。

posted @ 2021-04-11 19:13  19A1  阅读(99)  评论(0)    收藏  举报