BUAA_OO第一单元总结(多项式求导)

第一次作业

作业要求

第一次作业的主要任务是完成含有幂函数和常数的简单多项式求导,不需要判断表达式的合法性,整体比较简单

基于度量的程序结构分析

度量分析

各个方法的度量分析如下:

由于在Poly.addTerm()中分别判断了无指数幂,幂函数,常数的情况,以及有无项前符号,使用了过多了if_else结构,导致该函数的复杂度过高

类复杂度度量如下:

  • MainClass类中,主要是进行了表达式字符串处理,是以面向过程的方式进行的,复杂度很高。
  • Poly类复杂度高是由于addTerm()函数。

类图分析

  1. MainClass类:主要对表达式进行诸如去连续符号,空白字符,拆分项,以及顶层调用的工作
  2. Poly类:是一个由TreeMap维护的多项式类,负责保存不同指数的项的信息,采用TreeMap可以较好的进行同类项合并
  3. Term类:作为项的类,维护了系数和指数两个属性

通过Poly,Term实现了多项式求导,输出的分层,对于处理问题有一定的简化。并通过MainClass进行了顶层调用

Bug分析

自身bug

由于任务相对简单,本次作业在强测和互测中均未发现bug

互测

互测的hack数据都是通过python的re模块随机生成,并与sympy的求导结果对比制造的。
在互测结束以后,发现有几位同学的hack数据是诸如xxx*x......这样的数据引起的爆栈,这是随机化测试所难以发现的。

优缺点评价

  • 优点:
    • 通过构建Poly,Term两个类将表达式进行了分层处理。
    • 通过正则表达式的使用,简化了字符串的处理,减少了分支结构的使用
    • 利用TreeMap数据结构将不同的项进行了统一管理,有利于化简的进行
  • 缺点:
    • MainClass是以面向过程的方式构造的,而且Ploy和Term类仅仅进行了分层处理,面向对象的思想体现的不明显,可扩展性不足
    • 在对字符串进行处理的时候,是通过去掉空白字符和替换指数符号进行的,难以处理有表达式错误的情况,对于其后的迭代没有帮助,以至于第二次作业完全抛弃这种模式

第二次作业

作业要求

第二次作业的主要任务是完成含有幂函数,常数,简单正余弦的含有表达式的多项式求导,不需要判断表达式的合法性,本次作业相比第一次,有了嵌套结构,难度上有明显提升

基于度量的程序结构分析

度量分析

各个方法的度量分析如下:

  • Parse.Parse(String)函数在遍历表达式字符串时,提取出各个要素的信息,赋予一个标志,使用了很多if_else,使得结构比较复杂
  • Term.merge(ArrayList)函数,利用反射获得各个因子的类型,并进行化简合并,也同样使用了太多的if_else结构。可以考虑为每种因子提供一种方法,转移至更高的层次上去

类复杂度度量如下:

类图分析

  1. MainClass类负责顶层调用
  2. Parse类:
  • 初始化时读入字符串,并将各个要素进行分类,赋予一个枚举变量,便于其后的语义分析,例如SYMBOL,SIN,COS,EXP等等
  • 在进行语义分析的时候采用了递归下降的分析方法,parseExpression,parseTerm,parseFactor分别负责不同层次上的语法分析,并构建表达式树
  1. Expression类:
  • 内部以ArrayList数据结构维护了一个Term类的数组,分别表示了不同的项所构建的表达式。
  • derive函数负责表达式的求导,调用Term.derive实现对每一项进行求导
  • simplify函数负责表达式的化简,本次作业进行了不含表达式,各个其他因子指数相同的项的合并,在其中通过一个HashMap所维护的ID数据结构,进行合并,最终将values替换到Expression中的ArrayList实现化简
  1. Cos,Sin,Power,Const类都继承自Calculate抽象类
  • Calculate抽象类中定义了derive,toString方法,利用java的多态特性,简化程序结构
  • 这四个类都实现了自身的derive,toString方法,其中Sin,Cos类默认其中的表达式仅为"x"
  1. Tag,Type类:
  • 在Parse初始化的过程中给予各个要素的标签,以及其中记录了相应的信息,以供递归下降分析使用

Bug分析

自身bug

作业中存在一个bug,主要是因为在项化简和表达式合并时存在冲突引起的,在项化简过程中,省去了项中可能存在的因子"1",而在表达式合并的过程中,错误的认为每一项中一定存在常数项,而将常数项直接提取,进行相加,导致在强制转换时抛出异常。

互测

互测的hack数据都是通过python的re模块随机生成,并与sympy的求导结果对比制造的。
并手动构造了诸如(((((((((x))))))))))这样的特殊数据。

优缺点评价

  • 优点:
    • 本次作业相比第一次体现出了面向对象的设计方法,通过不同因子继承一个抽象类,实现了多态的利用。
    • 采用了递归下降的分析方法,程序的扩展性上有了很大的提升,便于后期的迭代。而且结构更加清晰。具有了分析异常数据的能力
    • 利用Expression和Term类的相互包含,实现了嵌套的求导
  • 缺点:
    • 程序中过多的使用了反射机制来判断一个对象所属的具体类,并进行分别地处理,if_else过多。
    • 程序的性能优化实现有限,对于稍复杂的情况难以进行有效化简

第三次作业

作业要求

第三次作业,相比第二次增加了错误表达式的判断,增加了Sin,Cos内部的表达式。总体上,跨度不大,只需要对Sin,Cos和递归下降分析进行调整即可。

基于度量的程序结构分析

度量分析

各个方法的度量分析如下


相比第二次,Paser类中有关递归下降的方法复杂度有所提升,主要是由于,异常格式判断这一功能的添加,使得if_else结构增多。

类复杂度度量如下:

相比第二次而言,Paser类的复杂度上升,其余类的变化不大。

类图分析


第三次作业主要对Sin,Cos,Parse进行了调整,其余类将不再赘述

  • Parse类:
    • 在parseExpression,parseTerm,parseFactor中加入了异常的判断。
  • Sin,Cos类:
    • 由第二次作业默认其中的表达式为"x",转换为一个Expression类,嵌套的求导,由这样的包含方式自动实现。

Bug分析

自身bug

作业中存在一个bug,主要是递归下降求导的过程中逻辑上的错误导致的,对于含有括号的表达式应该交给解析因子的函数进行解析,而不是由表达式直接处理。这样的问题会导致(x)*(sin(x))这样的表达式解析不全,引发错误。

互测

互测的hack数据都是通过python的re模块随机生成,并与sympy的求导结果对比制造的。
并手动构造了诸如sin(cos(sin(cos(x**2))))这样的特殊数据。

优缺点评价

  • 优点:
    • 本次作业相比第二次仅仅进行了部分调整,这一点相比第一次到第二次经历的重构充分体现了面向对象思想的可扩展性。
  • 缺点:
    • 程序中过多的使用了反射机制来判断一个对象所属的具体类,并进行分别地处理,if_else过多。
    • 程序的性能优化实现有限,对于稍复杂的情况难以进行有效化简

重构经历总结

这一单元的作业在第一和第二次之间进行了一次重构,程序结构被彻底地进行了重写。由之前地面向过程,转变到面向对象。由第一二次的类图就可以看出。

心得体会

这个单元的作业是初次接触面向对象进行的尝试,期间由第一次的面向过程的程序设计,到第二次作业难以进行,经历了一次痛苦的重构,转变为面向对象的设计思想,并在第三次作业中体会到了明显的优势。
另外的一点是,逻辑结构的不一定体现在构造出的类之中,第二次作业中提示可以构造一个嵌套类来实现表达式的嵌套求导,尝试了多次以后,依然无法实现这样的类。后来发现可以通过Term和Expression类的互相包含,通过类之间的关系实现不失为一种方案。
我觉得自身在提取代码的逻辑结构等方面还需要加强,代码虽然采用了面向对象的设计思想,但是调用关系有些复杂。

posted @ 2021-03-28 14:49  HyperCarrot  阅读(79)  评论(1编辑  收藏  举报