BUAA_OO_2022 Unit1 总结
1.架构分析
1.1 总体需求分析
Unit1中的三次作业归根结底就是得到一个带括号的复杂表达式,对其进行去括号,化简,合并操作。
1.2 hw1
第一次作业的目标是实现对单变量多项式的括号展开。在训练已经给出递归下降解析的情况下,本次作业的难度是比较低的。
UML类图
在第一次作业中,因为对继承,接口还不太熟悉,所以就直接采用训练题里的框架,并进行了一些填充,完成了第一次作业。在解析建树方面,根据设定的形式化表述,用Expr(表达式)存储Term(项),Term(项)存储三个因子:Expr(表达式因子),Number(常数因子),PowerFunc(变量因子)。建树的过程采用了训练中的思路用Parser和Lexer两个类递归下降分析拆解输入的表达式,这个大思路贯穿了三次作业。
建树完成后,接下来要考虑的就是如何遍历输出,考虑到每个项的最终形式可以表示为 系数*x**指数,我又创建了一个新的类Poly用于存储每个项的最终形式,Poly类中含有系数,指数以及符号。为了化简,我还创建了Polynomial类用于存储Poly。在遍历树的过程中,调用ergodic方法就可以获得一个Polynomial类。因为第一次作业时固有面向过程思维的影响,本应在遍历过程中合并更新状态的操作被我放到了MainClass中,遍历时只是单纯的用Polynomial把项以相同的形式统计起来,在Main里按照指数进行了化简。
在去括号化简完成后,我将表达式存到Ans类中,并调用out()方法进行最终简化输出。
度量分析
类复杂度分析:
OCavg
:计算每个类中所有非抽象方法的平均圈复杂度。 继承的方法不计算在内。
OCmax
:计算每个类中非抽象方法的最大圈复杂度。 继承的方法不计算在内。
WMC
: 计算每个类中方法的总圈复杂度。
class | OCavg | OCmax | WMC |
Ans | 4.666666666666667 | 7.0 | 14.0 |
expr.Expr | 1.8333333333333333 | 4.0 | 11.0 |
expr.Number | 1.6666666666666667 | 3.0 | 5.0 |
expr.PowerFunc | 1.6666666666666667 | 3.0 | 5.0 |
expr.Term | 2.0 | 4.0 | 10.0 |
Lexer | 2.1666666666666665 | 7.0 | 13.0 |
MainClass | 8.0 | 8.0 | 8.0 |
Parser | 3.75 | 8.0 | 15.0 |
polynomial.Poly | 1.5 | 3.0 | 9.0 |
polynomial.Polynomial | 1.6 | 4.0 | 8.0 |
Total | 98.0 | ||
Average | 2.3333333333333335 | 5.1 | 9.8 |
可以看到,Ans,Mainclass和Parser的复杂度比较高,Ans是因为对输出的判断采用了大量的if,Mainclass则是因为将化简过程实现在了这里导致的超高复杂度,Parser则是解析因子时没有把每种情况分出去而是共同处理了。
回过头看,当初写的时候还是没有摆脱面向过程的思维方式,只进行了大块的划分,对于很多操作,能一起写就一起写了,导致耦合度,复杂度都比较高。之后再完成作业时应当尽可能将各种方法抽象出来,而不是在一个方法中针对具体情况大量使用if-else,这样不光复杂度高,迭代也非常麻烦,需要直接修改而非新增。
方法复杂度分析:
CogC
:认知复杂性,随着每个控制结构的使用而增加,而且嵌套控制结构越多,认知复杂性就越高。
ev(G)
:本质复杂性是一种图论度量方法控制流的结构不良程度的方法。
iv(G)
:计算方法的设计复杂度。
v(G)
:计算每个非抽象方法的圈复杂度。 圈复杂度是对通过每个方法的不同执行路径数量的度量。
method | CogC | ev(G) | iv(G) | v(G) |
Ans.addAns(String, BigInteger, int) | 0.0 | 1.0 | 1.0 | 1.0 |
Ans.c(int) | 13.0 | 1.0 | 7.0 | 7.0 |
Ans.out() | 7.0 | 3.0 | 6.0 | 6.0 |
expr.Expr.addTerm(Term) | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Expr.ergodic() | 3.0 | 2.0 | 4.0 | 4.0 |
expr.Expr.Expr() | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Expr.setFirst(Term) | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Expr.setIndex(int) | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Expr.setSign(String) | 4.0 | 1.0 | 2.0 | 3.0 |
expr.Factor.ergodic() | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Number.ergodic() | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Number.Number(BigInteger) | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Number.setSign(String) | 4.0 | 1.0 | 2.0 | 3.0 |
expr.PowerFunc.ergodic() | 0.0 | 1.0 | 1.0 | 1.0 |
expr.PowerFunc.PowerFunc(int) | 0.0 | 1.0 | 1.0 | 1.0 |
expr.PowerFunc.setSign(String) | 4.0 | 1.0 | 2.0 | 3.0 |
expr.Term.addFactor(Factor) | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Term.ergodic() | 4.0 | 1.0 | 4.0 | 4.0 |
expr.Term.setFirst(Factor) | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Term.setSign(String) | 4.0 | 1.0 | 2.0 | 3.0 |
expr.Term.Term() | 0.0 | 1.0 | 1.0 | 1.0 |
Lexer.getNumber() | 2.0 | 1.0 | 3.0 | 3.0 |
Lexer.getPos() | 0.0 | 1.0 | 1.0 | 1.0 |
Lexer.judge() | 1.0 | 1.0 | 2.0 | 2.0 |
Lexer.Lexer(String) | 0.0 | 1.0 | 1.0 | 1.0 |
Lexer.next() | 10.0 | 2.0 | 6.0 | 9.0 |
Lexer.peek() | 0.0 | 1.0 | 1.0 | 1.0 |
MainClass.main(String[]) | 15.0 | 3.0 | 8.0 | 8.0 |
Parser.parseExpr() | 2.0 | 1.0 | 3.0 | 3.0 |
Parser.parseFactor() | 16.0 | 4.0 | 8.0 | 8.0 |
Parser.Parser(Lexer) | 0.0 | 1.0 | 1.0 | 1.0 |
Parser.parseTerm() | 2.0 | 1.0 | 3.0 | 3.0 |
polynomial.Poly.compareTo(Poly) | 2.0 | 3.0 | 2.0 | 3.0 |
polynomial.Poly.getCoefficient() | 0.0 | 1.0 | 1.0 | 1.0 |
polynomial.Poly.getIndex() | 0.0 | 1.0 | 1.0 | 1.0 |
polynomial.Poly.getSign() | 0.0 | 1.0 | 1.0 | 1.0 |
polynomial.Poly.Poly(BigInteger, int, String) | 0.0 | 1.0 | 1.0 | 1.0 |
polynomial.Poly.reverseSign() | 2.0 | 1.0 | 1.0 | 2.0 |
polynomial.Polynomial.addPoly(Poly) | 0.0 | 1.0 | 1.0 | 1.0 |
polynomial.Polynomial.addPolynomial(Polynomial) | 0.0 | 1.0 | 1.0 | 1.0 |
polynomial.Polynomial.copy(Polynomial) | 0.0 | 1.0 | 1.0 | 1.0 |
polynomial.Polynomial.getPolies() | 0.0 | 1.0 | 1.0 | 1.0 |
polynomial.Polynomial.multPolynomial(Polynomial) | 7.0 | 1.0 | 4.0 | 4.0 |
Total | 102.0 | 54.0 | 94.0 | 103.0 |
Average | 2.372093023255814 | 1.255813953488372 | 2.186046511627907 | 2.395348837209302 |
通过方法复杂度分析同样可以看出Ans,Mainclass和Parser的复杂度比较高。
第一次作业的架构的耦合度是比较高的,同时,因为在第一次作业时完全没有考虑有关迭代的问题,所以很多方面直接采用了固定的存储,判断,这导致在第二次作业时,进行了不小的修改,小规模的进行了重构。
整体而言,第一次作业的架构确实应付过去了,而且从大体思路上看,后续的迭代开发是不用推倒重来的,但是这个架构依然存在很多问题,最为致命的就是抽象程度不高,很多地方的判断存储形式完全围绕第一次作业为后续的迭代开发带来了不小的工作量。
1.3 hw2
第二次作业的目标是实现多项式的括号展开与函数调用、化简。虽然第一次作业的架构在细节方面非常不好,但是因为大体思路的一致性,我选择了在第一次作业的基础上进行迭代开发
UML类图
相较于第一次作业,第二次作业的复杂度有了较大的提升,其主要原因就在于多出了三角函数,自定义函数和求和函数。从类图中可以看出,虽然这次作业增加了三个因子,但是我只多出了Trigonometric这一个类,这是因为我将自定义函数和求和函数的解析放入了Parser中,同时可以看到Poly类的增量非常之多,这也是因为先前架构的问题导致的,因为时间不是很充裕所以没有修改。下面我将具体分析架构的迭代改进。
首先是解析过程,依然沿用了第一次作业的思路采用递归下降的方式进行解析。在Parser中我没有将各种类的解析放入它们本身的类中,而是直接放在Parser中处理了,这就使得我的类基本没有增加,但是Parser这个类却变得十分的臃肿。对于Expr和Term,解析方法不变,对于Factor,则是用if进行了选择,对于已有的因子,直接将第一次作业的处理封装了一下。对于新增的三角函数因子,大致处理与其他因子差别不大。比较大的改变是自定义函数和求和函数,我将这两个因子的式子单独提取出来作为一个表达式因子,同时用Hashmap记录映射关系,再次创建Parser进行多次解析,同时进行变量的替换,以此处理自定义函数和求和函数,这样的解决方式避开了深度克隆的问题。
然后是遍历输出,同第一次作业一样,每一层级返回的都是一个Polynomial类,Expr进行加法操作,Term进行乘法操作,与之前不一样的在于在Polynomial类中,对于加法和乘法,都会在最后调用一次化简方法,修改Polynomial类中的ArrayList,实现化简。
对于输出,因为在我的设计中,将输出单独分出来一个类,所以导致需要Poly类的各个参数,这导致Poly类也变得臃肿并且public,这是一个比较失败的设计,主要是第一次作业没有考虑到后续迭代Poly状态增加,第二次作业前面的实现消耗大量时间所以就没有将这里重构,而是大致沿用第一次作业的架构,只是多加了三角函数的输出判断。
度量分析
类复杂度分析:
class | OCavg | OCmax | WMC |
polynomial.Polynomial | 2.8333333333333335 | 7.0 | 17.0 |
polynomial.Poly | 2.5 | 11.0 | 30.0 |
Parser | 4.7272727272727275 | 10.0 | 52.0 |
Main | 3.0 | 3.0 | 3.0 |
Lexer | 2.8 | 9.0 | 14.0 |
expression.Trigonometric | 1.5384615384615385 | 4.0 | 20.0 |
expression.Term | 2.0 | 4.0 | 10.0 |
expression.PowerFunc | 1.4 | 3.0 | 7.0 |
expression.Number | 1.5 | 3.0 | 9.0 |
expression.Expr | 1.7142857142857142 | 4.0 | 12.0 |
expression.CustomFunction | 1.0 | 1.0 | 4.0 |
Ans | 5.4 | 12.0 | 27.0 |
Total | 205.0 | ||
Average | 2.5625 | 5.916666666666667 | 17.083333333333332 |
相较于第一次作业Parser,Polynomial,ans和poly的复杂度有所增加,main的复杂度大幅下降。Polynomial,ans和poly的复杂度属于正常增加,Parser则是因为没有将工作分出去,而是都由Parser独断导致了复杂度的进一步提升,这是设计上的问题。main类复杂度的下降是因为将输出的工作进一步分了出去,减少的不彻底则是因为增加了输入的处理而没有把输入的操作拆分出去。
方法复杂度分析:
method | CogC | ev(G) | iv(G) | v(G) |
Ans.sb(Poly) | 25.0 | 1.0 | 12.0 | 12.0 |
polynomial.Poly.mm() | 23.0 | 3.0 | 13.0 | 14.0 |
Parser.trigonometricFactor(HashMap<character, factor="">) | 20.0 | 1.0 | 14.0 | 14.0 |
Parser.powerFuncFactorr(Factor) | 18.0 | 1.0 | 10.0 | 10.0 |
polynomial.Polynomial.merge() | 15.0 | 3.0 | 7.0 | 8.0 |
Lexer.next() | 14.0 | 2.0 | 8.0 | 12.0 |
polynomial.Poly.judege(Poly) | 14.0 | 4.0 | 4.0 | 6.0 |
Ans.sbTrigonometric(Poly) | 13.0 | 1.0 | 7.0 | 7.0 |
polynomial.Polynomial.multPolynomial(Polynomial) | 13.0 | 1.0 | 6.0 | 6.0 |
Parser.parseFactor(HashMap<character, factor="">) | 12.0 | 7.0 | 10.0 | 10.0 |
Parser.customFagtor() | 11.0 | 3.0 | 7.0 | 7.0 |
Parser.sum(HashMap<character, factor="">) | 9.0 | 4.0 | 3.0 | 6.0 |
Ans.out() | 7.0 | 3.0 | 6.0 | 6.0 |
Parser.powerFuncFactor() | 4.0 | 2.0 | 3.0 | 3.0 |
expression.Expr.setSign(String) | 4.0 | 1.0 | 2.0 | 3.0 |
expression.Number.setSign(String) | 4.0 | 1.0 | 2.0 | 3.0 |
expression.PowerFunc.setSign(String) | 4.0 | 1.0 | 2.0 | 3.0 |
expression.Term.ergodic() | 4.0 | 1.0 | 4.0 | 4.0 |
expression.Term.setSign(String) | 4.0 | 1.0 | 2.0 | 3.0 |
expression.Trigonometric.setSign(String) | 4.0 | 1.0 | 2.0 | 3.0 |
Parser.exprFactor(HashMap<character, factor="">) | 3.0 | 1.0 | 3.0 | 3.0 |
expression.Expr.ergodic() | 3.0 | 2.0 | 4.0 | 4.0 |
expression.Trigonometric.compareTo(Trigonometric) | 3.0 | 4.0 | 3.0 | 4.0 |
Lexer.getNumber() | 2.0 | 1.0 | 3.0 | 3.0 |
Main.main(String[]) | 2.0 | 1.0 | 3.0 | 3.0 |
Parser.parseExpr(HashMap<character, factor="">) | 2.0 | 1.0 | 3.0 | 3.0 |
Parser.parseTerm(HashMap<character, factor="">) | 2.0 | 1.0 | 3.0 | 3.0 |
expression.Trigonometric.Trigonometric(String) | 2.0 | 1.0 | 1.0 | 2.0 |
expression.Trigonometric.clon() | 2.0 | 1.0 | 1.0 | 2.0 |
polynomial.Poly.compareTo(Poly) | 2.0 | 3.0 | 2.0 | 3.0 |
polynomial.Poly.reverseSign() | 2.0 | 1.0 | 1.0 | 2.0 |
Lexer.judge() | 1.0 | 1.0 | 2.0 | 2.0 |
expression.Number.changeNum(int) | 1.0 | 1.0 | 2.0 | 2.0 |
expression.Trigonometric.judge(Trigonometric) | 1.0 | 1.0 | 4.0 | 4.0 |
Ans.ans(ArrayList) | 0.0 | 1.0 | 1.0 | 1.0 |
Ans.existTrigonometric(Poly) | 0.0 | 1.0 | 1.0 | 1.0 |
Lexer.Lexer(String) | 0.0 | 1.0 | 1.0 | 1.0 |
Lexer.peek() | 0.0 | 1.0 | 1.0 | 1.0 |
Parser.Parser(Lexer) | 0.0 | 1.0 | 1.0 | 1.0 |
Parser.addCustomFunctions(String, String) | 0.0 | 1.0 | 1.0 | 1.0 |
expression.CustomFunction.addParameter(char) | 0.0 | 1.0 | 1.0 | 1.0 |
expression.CustomFunction.cursion() | 0.0 | 1.0 | 1.0 | 1.0 |
expression.CustomFunction.setExpr(Expr) | 0.0 | 1.0 | 1.0 | 1.0 |
expression.CustomFunction.setName(char) | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Expr.Expr() | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Expr.addTerm(Term) | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Expr.clone() | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Expr.getSign() | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Expr.setIndex(int) | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Factor.clon() | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Factor.ergodic() | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Number.Number(BigInteger) | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Number.clon() | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Number.ergodic() | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Number.getNum() | 0.0 | 1.0 | 1.0 | 1.0 |
expression.PowerFunc.PowerFunc(int) | 0.0 | 1.0 | 1.0 | 1.0 |
expression.PowerFunc.addIndex(int) | 0.0 | 1.0 | 1.0 | 1.0 |
expression.PowerFunc.clon() | 0.0 | 1.0 | 1.0 | 1.0 |
expression.PowerFunc.ergodic() | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Term.Term() | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Term.addFactor(Factor) | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Term.setFirst(Factor) | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Trigonometric.addIndex(int) | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Trigonometric.ergodic() | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Trigonometric.getBaseCoefficient() | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Trigonometric.getBaseIndex() | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Trigonometric.getIndex() | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Trigonometric.getType() | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Trigonometric.setBase(BigInteger, int) | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Trigonometric.setIndex(int) | 0.0 | 1.0 | 1.0 | 1.0 |
polynomial.Poly.Poly(BigInteger, int, String) | 0.0 | 1.0 | 1.0 | 1.0 |
polynomial.Poly.addTrigonometrics(Trigonometric) | 0.0 | 1.0 | 1.0 | 1.0 |
polynomial.Poly.getCoefficient() | 0.0 | 1.0 | 1.0 | 1.0 |
polynomial.Poly.getIndex() | 0.0 | 1.0 | 1.0 | 1.0 |
polynomial.Poly.getSign() | 0.0 | 1.0 | 1.0 | 1.0 |
polynomial.Poly.getTrigonometrics() | 0.0 | 1.0 | 1.0 | 1.0 |
polynomial.Poly.setCoefficient(BigInteger) | 0.0 | 1.0 | 1.0 | 1.0 |
polynomial.Poly.setSign(String) | 0.0 | 1.0 | 1.0 | 1.0 |
polynomial.Polynomial.addPoly(Poly) | 0.0 | 1.0 | 1.0 | 1.0 |
polynomial.Polynomial.addPolynomial(Polynomial) | 0.0 | 1.0 | 1.0 | 1.0 |
polynomial.Polynomial.copy(Polynomial) | 0.0 | 1.0 | 1.0 | 1.0 |
polynomial.Polynomial.getPolies() | 0.0 | 1.0 | 1.0 | 1.0 |
Total | 250.0 | 110.0 | 207.0 | 228.0 |
Average | 3.048780487804878 | 1.3414634146341464 | 2.524390243902439 | 2.7804878048780486 |
依然是因为某几个类中有抽象化程度过低,判断过多导致的部分复杂度较高。
1.4 hw3
第三次作业的目标是多层嵌套表达式和函数调用的括号展开与化简。
UML类图
第三次作业的设计同第二次作业差别不大,因为递归下降算法不太需要考虑括号嵌套的问题,比较麻烦的地方在于因为函数的调用,拷贝终究没能躲过去,而因为先前架构的问题导致自定义函数和求和函数既有重新解析,又有clone,显得比较怪异。对于三角函数类,因为有Polynomial这个独立的类,所以直接将base当成一个表达式解析,最后存成一个Polynomial类。因为第三次作业三教函数内部的复杂性,所以没有去进行过多的化简,对于复杂的三角函数放弃化简,所以剩下的部分大致与第二次类似。
度量分析
类复杂度分析:
class | OCavg | OCmax | WMC |
polynomial.Polynomial | 2.8333333333333335 | 7.0 | 17.0 |
polynomial.Poly | 1.5454545454545454 | 6.0 | 17.0 |
Parser | 3.769230769230769 | 8.0 | 49.0 |
Main | 3.0 | 3.0 | 3.0 |
Lexer | 2.6666666666666665 | 9.0 | 16.0 |
expression.Trigonometric | 1.5555555555555556 | 4.0 | 14.0 |
expression.Term | 2.0 | 4.0 | 12.0 |
expression.PowerFunc | 1.4 | 3.0 | 7.0 |
expression.Number | 1.8 | 3.0 | 9.0 |
expression.Expr | 1.75 | 4.0 | 14.0 |
Ans | 4.5 | 8.0 | 27.0 |
Total | 185.0 | ||
Average | 2.4342105263157894 | 5.363636363636363 | 16.818181818181817 |
因为在第三次作业中对部分方法进行了进一步抽象,所以复杂度有所降低。
方法复杂度分析:
method | CogC | ev(G) | iv(G) | v(G) |
Ans.sb(Poly) | 16.0 | 1.0 | 7.0 | 7.0 |
Ans.sbTrigonometric(Poly, boolean) | 15.0 | 3.0 | 7.0 | 8.0 |
polynomial.Polynomial.merge() | 15.0 | 3.0 | 7.0 | 8.0 |
Lexer.next() | 14.0 | 2.0 | 8.0 | 12.0 |
Parser.sum() | 13.0 | 5.0 | 5.0 | 8.0 |
polynomial.Polynomial.multPolynomial(Polynomial) | 13.0 | 1.0 | 6.0 | 6.0 |
Parser.parseFactor(HashMap<character, factor="">) | 12.0 | 7.0 | 10.0 | 10.0 |
Parser.customFagtor() | 11.0 | 3.0 | 7.0 | 7.0 |
Ans.out() | 9.0 | 3.0 | 8.0 | 8.0 |
expression.Trigonometric.ergodic() | 7.0 | 3.0 | 3.0 | 4.0 |
Ans.judge(Polynomial) | 6.0 | 4.0 | 1.0 | 9.0 |
polynomial.Poly.compareTo(Poly) | 6.0 | 6.0 | 4.0 | 6.0 |
Parser.getBigInteger() | 5.0 | 1.0 | 3.0 | 3.0 |
Parser.specialPowerFuncFactor(Factor) | 5.0 | 2.0 | 6.0 | 6.0 |
Parser.getIndex() | 4.0 | 1.0 | 3.0 | 3.0 |
Parser.powerFuncFactor() | 4.0 | 2.0 | 2.0 | 3.0 |
expression.Expr.setSign(String) | 4.0 | 1.0 | 2.0 | 3.0 |
expression.Number.setSign(String) | 4.0 | 1.0 | 2.0 | 3.0 |
expression.PowerFunc.setSign(String) | 4.0 | 1.0 | 2.0 | 3.0 |
expression.Term.ergodic() | 4.0 | 1.0 | 4.0 | 4.0 |
expression.Term.setSign(String) | 4.0 | 1.0 | 2.0 | 3.0 |
Parser.exprFactor(HashMap<character, factor="">) | 3.0 | 3.0 | 2.0 | 3.0 |
expression.Expr.ergodic() | 3.0 | 2.0 | 4.0 | 4.0 |
expression.Number.changeNum(int) | 3.0 | 1.0 | 3.0 | 4.0 |
Lexer.getNumber() | 2.0 | 1.0 | 3.0 | 3.0 |
Main.main(String[]) | 2.0 | 1.0 | 3.0 | 3.0 |
Parser.parseExpr(HashMap<character, factor="">) | 2.0 | 1.0 | 3.0 | 3.0 |
Parser.parseTerm(HashMap<character, factor="">) | 2.0 | 1.0 | 3.0 | 3.0 |
expression.Trigonometric.Trigonometric(String) | 2.0 | 1.0 | 1.0 | 2.0 |
expression.Trigonometric.clon() | 2.0 | 1.0 | 1.0 | 2.0 |
polynomial.Poly.reverseSign() | 2.0 | 1.0 | 1.0 | 2.0 |
Lexer.judge() | 1.0 | 1.0 | 2.0 | 2.0 |
Lexer.manynext(int) | 1.0 | 1.0 | 2.0 | 2.0 |
Parser.trigonometricFactor(HashMap<character, factor="">) | 1.0 | 1.0 | 2.0 | 2.0 |
expression.Expr.clon() | 1.0 | 1.0 | 2.0 | 2.0 |
expression.Term.clon() | 1.0 | 1.0 | 2.0 | 2.0 |
polynomial.Poly.judge(Poly) | 1.0 | 1.0 | 2.0 | 2.0 |
Ans.ans(ArrayList) | 0.0 | 1.0 | 1.0 | 1.0 |
Ans.existTrigonometric(Poly) | 0.0 | 1.0 | 1.0 | 1.0 |
Lexer.Lexer(String) | 0.0 | 1.0 | 1.0 | 1.0 |
Lexer.peek() | 0.0 | 1.0 | 1.0 | 1.0 |
Parser.Parser(Lexer) | 0.0 | 1.0 | 1.0 | 1.0 |
Parser.addCustomFunctions(String, String) | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Expr.Expr() | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Expr.addTerm(Term) | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Expr.getSign() | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Expr.multIndex(int) | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Expr.setIndex(int) | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Factor.clon() | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Factor.ergodic() | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Number.Number(BigInteger) | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Number.clon() | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Number.ergodic() | 0.0 | 1.0 | 1.0 | 1.0 |
expression.PowerFunc.PowerFunc(int) | 0.0 | 1.0 | 1.0 | 1.0 |
expression.PowerFunc.clon() | 0.0 | 1.0 | 1.0 | 1.0 |
expression.PowerFunc.ergodic() | 0.0 | 1.0 | 1.0 | 1.0 |
expression.PowerFunc.multIndex(int) | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Term.Term() | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Term.addFactor(Factor) | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Term.setFirst(Factor) | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Trigonometric.getIndex() | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Trigonometric.getPolynomial() | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Trigonometric.getType() | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Trigonometric.multIndex(int) | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Trigonometric.setBase(Factor) | 0.0 | 1.0 | 1.0 | 1.0 |
expression.Trigonometric.setIndex(int) | 0.0 | 1.0 | 1.0 | 1.0 |
polynomial.Poly.Poly(BigInteger, int, String) | 0.0 | 1.0 | 1.0 | 1.0 |
polynomial.Poly.addTrigonometrics(Trigonometric) | 0.0 | 1.0 | 1.0 | 1.0 |
polynomial.Poly.getCoefficient() | 0.0 | 1.0 | 1.0 | 1.0 |
polynomial.Poly.getIndex() | 0.0 | 1.0 | 1.0 | 1.0 |
polynomial.Poly.getSign() | 0.0 | 1.0 | 1.0 | 1.0 |
polynomial.Poly.getTrigonometrics() | 0.0 | 1.0 | 1.0 | 1.0 |
polynomial.Poly.setCoefficient(BigInteger) | 0.0 | 1.0 | 1.0 | 1.0 |
polynomial.Poly.setSign(String) | 0.0 | 1.0 | 1.0 | 1.0 |
polynomial.Polynomial.addPoly(Poly) | 0.0 | 1.0 | 1.0 | 1.0 |
polynomial.Polynomial.addPolynomial(Polynomial) | 0.0 | 1.0 | 1.0 | 1.0 |
polynomial.Polynomial.copy(Polynomial) | 0.0 | 1.0 | 1.0 | 1.0 |
polynomial.Polynomial.getPolies() | 0.0 | 1.0 | 1.0 | 1.0 |
Total | 204.0 | 112.0 | 176.0 | 206.0 |
Average | 2.6153846153846154 | 1.435897435897436 | 2.2564102564102564 | 2.641025641025641 |
从方法复杂度看,相较于第二次作业也有所降低。
2.Bug分析
2.1自身bug分析
第一次作业出现的问题是没有考虑括号结尾的情况,导致了越界访问。在Lexer类中,获取下一个元素时,对于空白符的跳过应该加上字符串长度的限制条件。
第二次作业出现的问题是三角函数的幂,在解析遍历的时候没有考虑,在输出的时候却针对幂有特定的输出,这就导致化简出现了错误。
第三次作业的问题有两个,一个是clone,一个是三角函数内部-x没有加括号。后者是因为对指导书的描述没有认真理解。前者则是完全的大意,在克隆的时候忽略了符号,又因为初始化为正号,所以带负号且需要clone的样例全部出现了错误。
三次作业的bug都是比较容易发现的bug,没有即使修复的原因主要是自己没有进行充分的测试,过于依赖提供的评测,之后的作业应该加强测试。还需注意的就是clone的完整性,在写作业的过程中已经因为不完全clone出现了问题进行了修改,但却没有修改完,始终没有加上符号的克隆,之后如果有需要clone的地方需要确保所有的状态都被clone。
2.2他人bug分析
出现的问题主要是因为考虑的情况不够全面。如结果为0无输出,三角函数幂相关的化简忘记考虑幂的奇偶,自定义函数代入顺序,求和函数上下界等等。除此之外还包含了少部分指导书理解问题,如三角函数内-x加括号等。
3.心得体会
经历了三次作业的洗礼,最为深刻的教训就是一定要把各种实现方法抽象化。抽象化的方法不仅简单易修改,而且复杂度低,在进行迭代时也不用反复考虑如何在那么庞大具体又臃肿的代码中加入新的条件。
其次就是在写之前一定要思考如何构建出一个好,易迭代的架构,思考每一步分给哪一个类比较好,以Unit1中作业为例,既然已经用一个新的类存储各个部分了,那么输出完全可以交给它来完成;对于输出,各个判断条件也可以通过不同的方法实现而不是大量的if-else;还有就是对于Parser,如果将因子的解析分到各个类中,那将会更加清晰明了且耦合度较低,而非现在这样完全混杂在了一起,难以修改。
还有就是一定要进行测试,有非常多的bug都可以通过简单的测试发掘出来,弱测只是对最基础的功能进行测试,不能过了弱测便以为万事大吉。
最后,希望第一单元的收获能让我更顺利的通过接下来几个单元,同时感谢老师以及助教的辛苦付出。