单元综述
单元总共为三次作业,难度递增。
-
第一次:简单多项式导函数的求解。
-
第二次:包含简单幂函数和简单正余弦函数的导函数的求解。
-
第三次:包含简单幂函数和简单正余弦函数的导函数的求解,加入了表达式因子。
从第一次作业到第三次作业,我逐渐根据题目的要求调整我的代码结构,在这之中也学习到了很多东西。以下是我对这三次作业的分析和总结。
一. 基于度量的程序结构分析
本文中基于度量分析的图片采用IDEA的Metrics生成。
分析结果解释的方法来源于博客(https://www.cnblogs.com/qianmianyu/p/8698557.html)
ev(G):即Essentail Complexity,用来表示一个方法的结构化程度,范围在之间,值越大则程序的结构越“病态”,其计算过程和图的“缩点”有关。
iv(G):即Design Complexity,用来表示一个方法和他所调用的其他方法的紧密程度,范围也在之间,值越大联系越紧密。
v(G):即循环复杂度,可以理解为穷尽程序流程每一条路径所需要的试验次数。
对于类,有OCavg和WMC两个项目,分别代表类的方法的平均循环复杂度和总循环复杂度。
第一次作业
类图
复杂度分析表
Method | ev(G) | iv(G) | v(G) |
---|---|---|---|
Derivation.isPoly(String) | 3 | 7 | 8 |
Derivation.main(String[]) | 1 | 1 | 1 |
Polynomial.Polynomial(String) | 1 | 10 | 10 |
Polynomial.addElement(BigInteger,BigInteger) | 1 | 2 | 2 |
Polynomial.getDerivation(Polynomial) | 4 | 6 | 7 |
Polynomial.printPoly(Polynomial) | 2 | 16 | 16 |
Class | OCavg | WMC |
---|---|---|
Derivation | 4 | 8 |
Polynomial | 7.25 | 29 |
分析
第一次作业中,初识OO,我只用了一个多项式类,基本上所有的操作都在多项式类中完成,平均复杂度较高。
第二次作业
类图
复杂度分析表
Method | ev(G) | iv(G) | v(G) |
---|---|---|---|
Derivation.end() | 1 | 1 | 1 |
Derivation.isPoly(String) | 3 | 6 | 7 |
Derivation.main(String[]) | 1 | 2 | 2 |
Polynomial.Derivation1(int) | 1 | 3 | 3 |
Polynomial.Derivation2(int) | 1 | 5 | 5 |
Polynomial.Derivation3(int) | 1 | 4 | 4 |
Polynomial.Derivation4(int) | 1 | 1 | 1 |
Polynomial.Polynomial(String) | 3 | 3 | 4 |
Polynomial.addElement(Term) | 3 | 6 | 6 |
Polynomial.getDerivation(Polynomial) | 1 | 11 | 11 |
Polynomial.getTerm(String[]) | 2 | 2 | 2 |
Polynomial.improve() | 9 | 17 | 17 |
Polynomial.printPoly(Polynomial) | 4 | 17 | 19 |
Polynomial.printXterm(int,int[]) | 1 | 10 | 10 |
Term.Term(String) | 6 | 10 | 10 |
Term.coeChange(String) | 1 | 1 | 1 |
Term.expChange(String,BigInteger[]) | 1 | 3 | 3 |
Term.getTermCoe() | 1 | 1 | 1 |
Term.getTermCosExp() | 1 | 1 | 1 |
Term.getTermSinExp() | 1 | 1 | 1 |
Term.getTermXExp() | 1 | 1 | 1 |
Class | OCavg | WMC |
---|---|---|
Derivation | 3.33 | 10 |
Polynomial | 4.91 | 54 |
Term | 2.57 | 18 |
分析
第二次作业中,我将第一次作业中的多项式类拆分成了项类和多项式类,在其中分别进行不同的操作,复杂度比第一次有了一些下降。不过有一些函数 ev(G) 值较大,比如 Polynomial.improve() ,应进行一些拆分。
第三次作业
类图
复杂度分析表
Method | ev(G) | iv(G) | v(G) |
---|---|---|---|
Cos.Cos(String) | 1 | 1 | 1 |
Cos.complexCos(String) | 5 | 10 | 11 |
Cos.getDerivation(String) | 1 | 4 | 5 |
Cos.judgeExp(String) | 1 | 2 | 2 |
Expression.Expression(String) | 1 | 1 | 1 |
Expression.delChange(String) | 1 | 7 | 8 |
Expression.getDerivation(String) | 1 | 6 | 9 |
Expression.getTerm(String[]) | 1 | 9 | 11 |
Main.check(String) | 5 | 14 | 16 |
Main.end() | 1 | 1 | 1 |
Main.main(String[]) | 1 | 9 | 9 |
Num.Num(String) | 1 | 1 | 1 |
Num.getDerivation(String) | 1 | 1 | 1 |
Sin.Sin(String) | 1 | 1 | 1 |
Sin.complexSin(String) | 5 | 10 | 11 |
Sin.getDerivation(String) | 1 | 4 | 5 |
Sin.judgeExp(String) | 1 | 2 | 2 |
Term.Term(String) | 1 | 1 | 1 |
Term.addList(String) | 1 | 5 | 5 |
Term.getDerivation(String) | 1 | 5 | 5 |
Term.getFactor(String[]) | 1 | 6 | 8 |
Term.isCos(String) | 2 | 1 | 2 |
Term.isExpression(String) | 2 | 1 | 2 |
Term.isNum(String) | 2 | 1 | 2 |
Term.isSin(String) | 2 | 1 | 2 |
Term.isX(String) | 2 | 1 | 2 |
X.X(String) | 1 | 1 | 1 |
X.getDerivation(String) | 1 | 4 | 5 |
Class | OCavg | WMC |
---|---|---|
Cos | 4.25 | 17 |
Expression | 5.75 | 23 |
Main | 7 | 21 |
Num | 1 | 2 |
Sin | 4.25 | 17 |
Term | 3.11 | 28 |
X | 3 | 6 |
分析
第三次作业我建了一个Element父类,将所有操作类型作为子类。在 ev(G) 值上相较于上次有所优化。
二. 分析自己程序的bug
第一次作业
第一次作业中,我的 bug 来源于正则表达式的错误。我用于判断输入格式的正则表达式忽略了开头空格的情况,以及把一个 “+” 误写成了 “*”。
虽然看似不是什么大问题,但这确实体现了我在写程序的时候不够细致,测试自己的程序的时候用例的构造也不够全面。感觉这些错误对于一个程序来说还是挺要命的。
第二次作业
第二次作业中,我的 bug 来源于判断格式结束之后的 replace 环节。我在 replace 过程中将两个相同的 replace 语句放在了一起而没有循环替换,导致最后有些连在一起的运算符号并没有被完全替换,进而导致在运算中出现了错误。
这一次的错误还是因为我在测试中测试的不够全面,写代码的时候没有考虑到这方面的问题,以后应该更加全面的考虑问题。
第三次作业
第三次作业中,我的 bug 来源于我的程序无法正确处理 x*(2*x-3*x)
此类型的情况,因为我的代码在第一个因子已经解决,也就是剩余 (2*x-3*x)
时,在 “*” 处将剩余表达式拆分。在检查代码时,我发现这个问题的原因是由于我在一个循环需要重新开始时将循环变量 i 赋值为了 0,而在循环开始时, i 就会直接由于 i++
的原因直接变为1,而没有经历 i = 0
的过程。这个问题直接导致我的程序无法处理后续的表达式,从而报出异常。
我的另一个 bug 是由于正负号的问题,我在判断是否需要提出符号的时候没有考虑去掉的括号是否是首尾括号,因此导致了我多提出了一个符号,最终的表达式也因此多了一个符号。
这次的问题确实都出现在程序设计中,虽然确实测了很多的样例,但由于情况确实比较多,没有测出这两个问题。以后还是应该在写程序的时候多加注意,防止这种逻辑不严谨导致的错误。
三. 互测DEBUG
在每一次作业的互测中,我的样例主要来源于两个方面。一是来源于我自己在编程序的过程中没有想到的点,也就是在debug过程中发现的问题。二是来源于在互测过程中构造的样例。
样例的构造
在前两次作业中,我有许多互测用例的构造都基于对于输入输出结构的判断。比如在不同位置加入空格,多余的运算符号等。其余用例针对于功能的完善,测试程序能否根据输入的不同搭配得出正确的结果。
最后一次作业中,我的用例都是依然是针对上文中提到的这两个方面。但由于要求不考虑对于WRONG FORMAT的检查,我对于输入输出结构的判断主要集中于测试程序是否会对正确格式的样例输出WRONG FORMAT。其中,包括一些在格式判断中可能出现,比如括号的嵌套,表达式因子的嵌套相乘或相加,以及对于三角函数括号内部格式的判断等。由于这次作业的情况比较复杂,对于功能完善性的测试也非常重要。在测试时,主要关注对于边界情况的考虑,比如 sin(x)^+10000
;以及输出结果正负号的正确性。
四. Applying Creational Pattern
在我这三次的作业中,我逐渐将原来只有一个多项式大类拆分成了许多个因子小类,可以说在一定程度上减少了每一类的代码量,程序看起来也比之前清晰了许多。
在互测中看到一些大佬的代码,发现他们对于不同的计算方法也分了几类。感觉下次自己也可以做这样的尝试,可以避免自己计算的函数过于复杂。
五. 总结
总而言之,在这一单元里,我随着学习的进行逐渐改善着我的代码,对于对象的使用也有了初步的认知。可以说还是很有收获吧。