BUAA_OO_2022 第一单元总结

BUAA_OO_2022 第一单元总结

前言

总体而言,第一单元的三次作业是通过对表达式的展开,实现对面向对象思想的逐渐理解过程,从面向过程的思维模式转变为面向对象的思维模式。
这三次作业基本实现了迭代开发,但仍有一些问题需要进一步解决。

1. 第一次作业分析

第一次作业主要实现的是对含有一层括号表达式的简单化简。由于对形式化表述以及对面向对象思想的不太理解,首先使用是预解析模式完成作业,但仍为面向过程实现,不符合题目要求。最后的实现方法是将表达式分层次进行解析,采用递归下降的方式逐一解析4个不同层级的内容。

1.1 UML类图

1.2 具体分析

第一次作业中最基础的类被我称为Number类,这个类中包含基本项中的内容,形如a*x**b,并添加一个因子tag用以保存这个基本类之前的运算符。在高层级的每个类中都使用ArrayList数组存储比这个类级数低的所有项。这种在每个类中创建新的容器类的方法并不十分可取,这对于后续实验的迭代开发有很大的阻力,也导致我在实现第二次作业时需要大范围重构代码。

在整个解析和计算过程中,我并未进行表达式的合并和化简,对于其中的乘法计算使用的方法为标记已经使用的数据和未使用的数据,最后整合到ArrayList中。对于化简和同类项的合并问题,我在解析结束之后统一使用mody函数进行解决。这种方式可以算是一种偷懒的做法,在计算时不用考虑化简,最后进行整体化简,这种方法对第一次和第二次作业的实现没有什么问题,因为比较的项都很有规律且比较简单,但却对第三次作业的实现留下了较大的隐患。

1.3 方法复杂度分析

method CogC ev(G) iv(G) v(G)
expr.Base.addExpr(Expr) 1.0 1.0 2.0 2.0
expr.Base.addFactor(Number) 0.0 1.0 1.0 1.0
expr.Base.Base() 0.0 1.0 1.0 1.0
expr.Base.getNumbers() 0.0 1.0 1.0 1.0
expr.Base.powPoly(Expr, int) 26.0 1.0 10.0 10.0
expr.Expr.addTerm(boolean, Term) 6.0 1.0 4.0 4.0
expr.Expr.Expr() 0.0 1.0 1.0 1.0
expr.Expr.getNumbers() 0.0 1.0 1.0 1.0
expr.Expr.getTerms() 0.0 1.0 1.0 1.0
expr.Number.getNum() 0.0 1.0 1.0 1.0
expr.Number.getTag() 0.0 1.0 1.0 1.0
expr.Number.getZhishu() 0.0 1.0 1.0 1.0
expr.Number.Number(BigInteger, int) 0.0 1.0 1.0 1.0
expr.Number.setNum(BigInteger) 0.0 1.0 1.0 1.0
expr.Number.setTag(int) 0.0 1.0 1.0 1.0
expr.Number.setZhishu(int) 0.0 1.0 1.0 1.0
expr.Number.toString() 0.0 1.0 1.0 1.0
expr.Term.addFactor(Base) 17.0 1.0 8.0 8.0
expr.Term.getBases() 0.0 1.0 1.0 1.0
expr.Term.getNumbers() 0.0 1.0 1.0 1.0
expr.Term.Term() 0.0 1.0 1.0 1.0
Lexer.getNumber() 2.0 1.0 3.0 3.0
Lexer.Lexer(String) 0.0 1.0 1.0 1.0
Lexer.next() 3.0 2.0 3.0 4.0
Lexer.peek() 0.0 1.0 1.0 1.0
MainClass.change(String) 2.0 1.0 2.0 3.0
MainClass.main(String[]) 4.0 1.0 3.0 3.0
MainClass.mody(Expr) 9.0 1.0 6.0 6.0
MainClass.print(Number) 16.0 1.0 13.0 13.0
Parser.parseBase() 5.0 1.0 3.0 3.0
Parser.parseExpression() 2.0 1.0 3.0 3.0
Parser.parseFactor() 25.0 1.0 9.0 9.0
Parser.Parser(Lexer) 0.0 1.0 1.0 1.0
Parser.parseTerm() 1.0 1.0 2.0 2.0
Total 119.0 35.0 91.0 93.0
Average 3.5 1.0294117647058822 2.676470588235294 2.735294117647059

总体而言第一次作业的复杂度不算高,但相关函数如解析因子函数parseFactor,表达式的乘方计算函数powPoly复杂度较高。

1.4 类度量

Class OCavg OCmax WMC
expr.Base 3.0 10.0 15.0
expr.Expr 1.75 4.0 7.0
expr.Number 1.0 1.0 8.0
expr.Term 2.75 8.0 11.0
Lexer 2.0 4.0 8.0
MainClass 6.0 13.0 24.0
Parser 3.4 9.0 17.0
Total 90.0
Average 2.6470588235294117 7.0 12.857142857142858

1.5 作业分析

第一次作业中强测和互测都被hack了没考虑到的(num)**num的情况,在互测过程中发现其他同学对BigInterger等特殊数据的处理并不完善。总体而言,在完成第一次作业时还是对面向对象方式的探索过程,主要保证的是结果的正确性,但却遗漏了对表达式指数情况的化简,而且在最终结果中,第一项之前还存在着相应的正负号,结果的性能也不高。

2. 第二次作业分析

第二次作业在第一次作业的基础上新增了三角函数因子、自定义函数项以及求和函数。为尽量减少对代码的大量重构,我在第一次作业的基础上进行了修改,整体思路和第一次相同,但具体实现上做了一定的优化。

2.1 UML 类图

2.2 具体分析

2.2.1 三角函数

对于新增的三角函数因子,我采用和第一次作业类似的方式进行存储,通过构造一个大的容器类,其中包含了两种不同的三角函数因子以及普通的系数和x的指数。在对三角函数内部的因子进行解析时,将内部的因子直接看成内部表达式的第一个容器中x对应的系数和指数,这种方式可以规整的对三角函数进行统一处理。

2.2.2 自定义函数和求和函数

为最大程度的实现第一次作业框架的复用,我在将表达式真正进行括号化简之前,将表达式中的所有自定义函数以及求和函数进行展开,将含有嵌套括号的表达式提交到真正解析的函数中。

对于新增的自定义函数的定义和调用,在定义过程中直接使用了正则表达式匹配进行相关项的分离。在自定义函数的调用过程中,我首先想到的方法也是通过正则表达式分离,最后发现无法处理例如sin(x)等出现在函数实参中的情况,最终使用类似栈的思想实现函数实参的分离。

对于求和函数的解析大体上和自定义函数相同,也是使用栈的思想将所有的元素分离开。但要特别注意sin的替换,以及对每个i替换后进行括号的添加,避免i**3等情况下错误的出现。

由于这两种函数的处理都需要对内容进行多层括号包装,在后续处理中要实现对多层括号处理的支持。

2.2.3 表达式化简

这次作业中,我将第一次作业中的化简函数抽离为方法类,实现可调控的化简。事实证明,化简版本并未成功,在使用化简函数后,项目代码始终不能通过最后一个测试点,最后只能提交未化简版本,算是这次作业的遗憾,在互测结束后进行强测样例复查时才发现最终问题。

2.3 方法复杂度分析

method CogC ev(G) iv(G) v(G)
Base.addExpr(Expr) 1.0 1.0 2.0 2.0
Base.addFactor(Container) 0.0 1.0 1.0 1.0
Base.Base() 0.0 1.0 1.0 1.0
Base.getContainers() 0.0 1.0 1.0 1.0
Base.powPoly(Expr, BigInteger) 46.0 1.0 13.0 13.0
Container.addCosclasses(Cosclass) 0.0 1.0 1.0 1.0
Container.addSinclasses(Sinclass) 0.0 1.0 1.0 1.0
Container.Container() 0.0 1.0 1.0 1.0
Container.getCoef() 0.0 1.0 1.0 1.0
Container.getCosclasses() 0.0 1.0 1.0 1.0
Container.getSinclasses() 0.0 1.0 1.0 1.0
Container.getTag() 0.0 1.0 1.0 1.0
Container.getXexpo() 0.0 1.0 1.0 1.0
Container.setCoef(BigInteger) 0.0 1.0 1.0 1.0
Container.setCosclasses(ArrayList) 0.0 1.0 1.0 1.0
Container.setSinclasses(ArrayList) 0.0 1.0 1.0 1.0
Container.setTag(int) 0.0 1.0 1.0 1.0
Container.setXexpo(BigInteger) 0.0 1.0 1.0 1.0
Cosclass.Cosclass() 0.0 1.0 1.0 1.0
Cosclass.getCoscoef() 0.0 1.0 1.0 1.0
Cosclass.getCosexpo() 0.0 1.0 1.0 1.0
Cosclass.getCossexpo() 0.0 1.0 1.0 1.0
Cosclass.setCoscoef(BigInteger) 0.0 1.0 1.0 1.0
Cosclass.setCosexpo(BigInteger) 0.0 1.0 1.0 1.0
Cosclass.setCossexpo(BigInteger) 0.0 1.0 1.0 1.0
Customfunc.Customfunc() 0.0 1.0 1.0 1.0
Customfunc.define(String) 8.0 1.0 5.0 6.0
Customfunc.getType() 0.0 1.0 1.0 1.0
Customfunc.parseString(String, int) 10.0 1.0 8.0 9.0
Expr.addTerm(boolean, Term) 6.0 1.0 4.0 4.0
Expr.Expr() 0.0 1.0 1.0 1.0
Expr.getContainers() 0.0 1.0 1.0 1.0
Expr.getTerms() 0.0 1.0 1.0 1.0
Lexer.getNumber() 2.0 1.0 3.0 3.0
Lexer.Lexer(String) 0.0 1.0 1.0 1.0
Lexer.next() 5.0 2.0 5.0 6.0
Lexer.peek() 0.0 1.0 1.0 1.0
Main.change(String) 2.0 1.0 2.0 3.0
Main.main(String[]) 24.0 1.0 14.0 14.0
Main.mody(Expr) 19.0 4.0 11.0 11.0
Main.print(Container) 6.0 1.0 4.0 4.0
Main.print2(Container) 53.0 1.0 18.0 18.0
Modify.mody2(Expr) 31.0 1.0 14.0 14.0
Modify.mody3(Expr) 52.0 1.0 16.0 18.0
Parser.parseBase() 9.0 1.0 4.0 4.0
Parser.parseExpression() 9.0 1.0 5.0 7.0
Parser.parsemi(boolean, BigInteger, BigInteger) 0.0 1.0 1.0 1.0
Parser.Parser(Lexer) 0.0 1.0 1.0 1.0
Parser.parseSimple() 26.0 1.0 12.0 12.0
Parser.parseTerm() 1.0 1.0 2.0 2.0
Parser.parsetri(boolean, String, Container, BigInteger) 0.0 1.0 1.0 1.0
Simplefunc.setSimple(boolean, BigInteger, BigInteger) 2.0 1.0 2.0 2.0
Sinclass.getSincoef() 0.0 1.0 1.0 1.0
Sinclass.getSinexpo() 0.0 1.0 1.0 1.0
Sinclass.getSinsexpo() 0.0 1.0 1.0 1.0
Sinclass.setSincoef(BigInteger) 0.0 1.0 1.0 1.0
Sinclass.setSinexpo(BigInteger) 0.0 1.0 1.0 1.0
Sinclass.setSinsexpo(BigInteger) 0.0 1.0 1.0 1.0
Sinclass.Sinclass() 0.0 1.0 1.0 1.0
Sumfunc.parsesum(String, int) 9.0 1.0 6.0 7.0
Sumfunc.Sumfunc() 0.0 1.0 1.0 1.0
Term.addFactor(Base) 37.0 1.0 12.0 12.0
Term.getBases() 0.0 1.0 1.0 1.0
Term.getContainers() 0.0 1.0 1.0 1.0
Term.Term() 0.0 1.0 1.0 1.0
Trigfunc.setTri(boolean, String, Container, BigInteger) 4.0 1.0 4.0 4.0
Total 362.0 70.0 210.0 219.0
Average 5.484848484848484 1.0606060606060606 3.1818181818181817 3.3181818181818183

在代码分析中可以直观的看出,复杂度较高的方法是主要是加减计算类以及乘方计算类。这主要是因为加减乘乘方时,我首先需要对所有用到的数据进行复制然后再进行运算,相对而言写法复杂。

2.4 类度量

class OCavg OCmax WMC
Base 3.6 13.0 18.0
Container 1.0 1.0 13.0
Cosclass 1.0 1.0 7.0
Customfunc 4.25 9.0 17.0
Expr 1.75 4.0 7.0
Lexer 2.25 5.0 9.0
Main 9.6 18.0 48.0
Modify 12.0 12.0 24.0
Parser 3.5714285714285716 11.0 25.0
Simplefunc 2.0 2.0 2.0
Sinclass 1.0 1.0 7.0
Sumfunc 4.0 7.0 8.0
Term 3.75 12.0 15.0
Trigfunc 4.0 4.0 4.0
Total 204.0
Average 3.090909090909091 7.142857142857143 14.571428571428571

2.5 作业分析

由于这次作业提交版本并没有使用化简,强测和互测都没有问题,也没有hack到其他同学,但对代码的优化应该实现。

3. 第三次作业分析

第三次作业实现自定义函数嵌套调用以及处理嵌套括号的问题,由于在第二次作业中,我已经实现了对嵌套括号的处理,所以只需处理嵌套函数的问题。

3.1 UML类图

3.2 具体分析

这次作业相较于第二次作业修改的地方主要是三角函数中的内容物。这次直接使用循环调用表达式,将表达式保存在三角函数中。对于各种计算方法,我在容器类container中实现对一个项中所有数据的深拷贝,避免出现乘除计算错误。在表达式合并时,通过重写容器类的tostring函数实现对项的简化判断。

对于嵌套函数的处理,我主要是层层分离的写法。通过依次扫描表达式识别出需要化简的部分,层层加括号实现对函数的去除。

3.3 方法复杂度分析

method CogC ev(G) iv(G) v(G)
src.Base.addCos(Cosclass) 0.0 1.0 1.0 1.0
src.Base.addExpr(Expr) 21.0 1.0 8.0 12.0
src.Base.addFactor(Container) 0.0 1.0 1.0 1.0
src.Base.addSin(Sinclass) 0.0 1.0 1.0 1.0
src.Base.Base() 0.0 1.0 1.0 1.0
src.Base.getContainers() 0.0 1.0 1.0 1.0
src.Base.powPoly(Expr, BigInteger) 51.0 1.0 14.0 14.0
src.Container.addCosclasses(Cosclass) 0.0 1.0 1.0 1.0
src.Container.addSinclasses(Sinclass) 0.0 1.0 1.0 1.0
src.Container.Container() 0.0 1.0 1.0 1.0
src.Container.copyAll(ArrayList) 7.0 1.0 4.0 4.0
src.Container.getCoef() 0.0 1.0 1.0 1.0
src.Container.getCosclasses() 0.0 1.0 1.0 1.0
src.Container.getSinclasses() 0.0 1.0 1.0 1.0
src.Container.getTag() 0.0 1.0 1.0 1.0
src.Container.getXexpo() 0.0 1.0 1.0 1.0
src.Container.setCoef(BigInteger) 0.0 1.0 1.0 1.0
src.Container.setCosclasses(ArrayList) 0.0 1.0 1.0 1.0
src.Container.setSinclasses(ArrayList) 0.0 1.0 1.0 1.0
src.Container.setTag(int) 0.0 1.0 1.0 1.0
src.Container.setXexpo(BigInteger) 0.0 1.0 1.0 1.0
src.Container.toString() 4.0 1.0 4.0 4.0
src.Cosclass.addCos(Container) 0.0 1.0 1.0 1.0
src.Cosclass.Cosclass() 0.0 1.0 1.0 1.0
src.Cosclass.getCoscs() 0.0 1.0 1.0 1.0
src.Cosclass.getExpo() 0.0 1.0 1.0 1.0
src.Cosclass.setCoscs(ArrayList) 0.0 1.0 1.0 1.0
src.Cosclass.setExpo(BigInteger) 0.0 1.0 1.0 1.0
src.Cosclass.tostr() 1.0 1.0 1.0 2.0
src.Cosclass.toString() 1.0 1.0 2.0 2.0
src.Customfunc.Customfunc() 0.0 1.0 1.0 1.0
src.Customfunc.define(String) 8.0 1.0 5.0 6.0
src.Customfunc.getType() 0.0 1.0 1.0 1.0
src.Customfunc.parseString(String, int) 17.0 3.0 12.0 14.0
src.Expr.addTerm(boolean, Term) 28.0 1.0 11.0 14.0
src.Expr.Expr() 0.0 1.0 1.0 1.0
src.Expr.getContainers() 0.0 1.0 1.0 1.0
src.Expr.getTerms() 0.0 1.0 1.0 1.0
src.Expr.toString() 1.0 1.0 2.0 2.0
src.Lexer.getNumber() 2.0 1.0 3.0 3.0
src.Lexer.Lexer(String) 0.0 1.0 1.0 1.0
src.Lexer.next() 5.0 2.0 5.0 6.0
src.Lexer.peek() 0.0 1.0 1.0 1.0
src.Main.change(String) 2.0 1.0 2.0 3.0
src.Main.main(String[]) 20.0 1.0 12.0 12.0
src.Parser.parseBase() 9.0 1.0 4.0 4.0
src.Parser.parseExpression() 9.0 1.0 5.0 7.0
src.Parser.parsemi(boolean, BigInteger, BigInteger) 0.0 1.0 1.0 1.0
src.Parser.Parser(Lexer) 0.0 1.0 1.0 1.0
src.Parser.parseSimple() 26.0 1.0 12.0 12.0
src.Parser.parseTerm() 1.0 1.0 2.0 2.0
src.Parser.parsetri(boolean, String, ArrayList, BigInteger) 0.0 1.0 1.0 1.0
src.Simplefunc.setSimple(boolean, BigInteger, BigInteger) 2.0 1.0 2.0 2.0
src.Sinclass.addSin(Container) 0.0 1.0 1.0 1.0
src.Sinclass.getExpo() 0.0 1.0 1.0 1.0
src.Sinclass.getSins() 0.0 1.0 1.0 1.0
src.Sinclass.setExpo(BigInteger) 0.0 1.0 1.0 1.0
src.Sinclass.setSins(ArrayList) 0.0 1.0 1.0 1.0
src.Sinclass.Sinclass() 0.0 1.0 1.0 1.0
src.Sinclass.tostr() 1.0 1.0 1.0 2.0
src.Sinclass.toString() 1.0 1.0 2.0 2.0
src.Sumfunc.parsesum(String, int) 9.0 1.0 6.0 7.0
src.Sumfunc.Sumfunc() 0.0 1.0 1.0 1.0
src.Term.addCos(Cosclass) 0.0 1.0 1.0 1.0
src.Term.addFactor(Base) 38.0 1.0 12.0 12.0
src.Term.addSin(Sinclass) 0.0 1.0 1.0 1.0
src.Term.getBases() 0.0 1.0 1.0 1.0
src.Term.getContainers() 0.0 1.0 1.0 1.0
src.Term.Term() 0.0 1.0 1.0 1.0
src.Trigfunc.setTri(boolean, String, ArrayList, BigInteger) 4.0 1.0 4.0 4.0
Total 268.0 73.0 181.0 198.0
Average 3.8285714285714287 1.042857142857143 2.585714285714286 2.8285714285714287

和第二次作业类似,复杂度较高的方法仍为计算乘法和乘方类方法。

3.4 类度量

class OCavg OCmax WMC
src.Base 3.857142857142857 14.0 27.0
src.Container 1.4 4.0 21.0
src.Cosclass 1.25 2.0 10.0
src.Customfunc 5.25 13.0 21.0
src.Expr 3.2 11.0 16.0
src.Lexer 2.25 5.0 9.0
src.Main 7.0 12.0 14.0
src.Parser 3.5714285714285716 11.0 25.0
src.Simplefunc 2.0 2.0 2.0
src.Sinclass 1.25 2.0 10.0
src.Sumfunc 4.0 7.0 8.0
src.Term 2.8333333333333335 12.0 17.0
src.Trigfunc 4.0 4.0 4.0
Total 184.0
Average 2.6285714285714286 7.615384615384615 14.153846153846153

3.5 作业分析

由于第二次作业基本实现相关功能,因此相对而言第三次作业是最轻松的。但由于之前在自定义函数中使用,分割相应的项,这种方法没有考虑到函数多参数的情况,直接导致强测测试点的错误。而且在sum的处理中括号添加的不够,导致i取负数时被hack多次。

4. 总结

4.1 数据测试

这一单元的测试中我主要还是使用手动输入的方式进行覆盖,这种方式并不能完全覆盖所有的结果,因此需要改进,可以尝试编写并使用自动化评测。

4.2 心得体会

第一单元主要还是对面向对象思想以及Java基础语法的理解和应用。

不足之处则是需要不断的熟悉相关代码以及语法内容,在使用时才能得心应手。而且需要有更好的测试特殊情况、增加有效覆盖的方式。

posted @ 2022-03-26 09:41  tiderem  阅读(15)  评论(0编辑  收藏  举报