BUAA OO 第一单元作业总结

BUAA OO 第一单元作业总结

第一次作业

作业构思

第一次作业要求实现对单变量因子的表达式进行括号展开、合并、化简,本人考虑到后续迭代的要求,选择了递归下降的方式解析表达式,单独建一个Lexer类和Parse类,分别负责提取表达式的各部分和解析,同时建立了三层表达式树,具体如下:

|- MainClass:主类
|- Lexer:表达式词法提取器
|- Parser: 表达式解析器
|- expr (package):表达式分解类
	|- Expr: 表达式类
	|- Term: 项类
	|- Poly: 多项式类,同时也是因子类

为了方便处理因子、项、项与项之间存在多个符号的问题,Expr表达式类以List数组存储项与项之间的符号;Term类以String类型存储项的符号;Poly类以HashMap存储自变量x的指数和系数(方便合并同类项),系数的正负就是因子的正负.

UML类图

由于因子全部以Poly多项式类形式存储,故没有继承和接口。

将几个重要的方法解释一下:

Expr.expPoly():实现对表达式因子幂次方的展开,返回一个多项式类(表达式因子)

Expr\Term.makePoly():将表达式或项转换为多项式类。

Poly.add,mul,sub:实现多项式类之间的加减乘运算。

度量分析

复杂度分析由idea的插件MetricsReload生成,手动进行了分析。

OCavg代表类的方法的平均循环复杂度,OCmax代表类的方法的最大循环复杂度,WMC代表类的总循环复杂度。

ev(G)基本复杂度,衡量程序非结构化程度,基本复杂度高意味着非结构化程度高,难以模块化和维护。

iv(G)模块设计复杂度,衡量模块判定结构,即模块和其他模块的调用关系;模块设计复杂度高意味模块耦合度高,这将导致模块难于隔离、维护和复用。

v(G)圈复杂度,衡量一个模块判定结构的复杂程度,根据程序从开始到结束的线性独立路径数量计算得到,圈复杂度越高,代码越复杂越难维护。

  • 所有类的复杂度分析

下面只取两个循环复杂度最高的类

  • Expr类

可以看到复杂度最高的方法为toString()方法,该方法的作用为将表达式的各个项转化为字符串,同时进行化简。由于化简和输出只在此类实现,故该方法的认知复杂度、模块设计复杂度、圈复杂度都比较高,在之后的作业中进行了优化。

  • Parser类

可以看到导致Parser类复杂度高的原因在于parseFactor()方法耦合度过高,原因是该方法解析了所有类型的因子,并没有分开写,这在之后的作业里进行了改善。

第二次作业

作业构思

第二次作业相比第一次作业,增加了三角函数、自定义函数和求和函数,跨度比较大,但由于第一次作业使用递归下降的思路,过渡到第二次作业增加了函数类和函数解析类,负责预处理自定义函数和求和函数;PolyKey类,是多项式Poly类中map的键,保存项里x的指数和三角函数;Tri三角函数类,保存三角函数类型和内部的因子。

|- MainClass:主类
|- Lexer:表达式词法提取器
|- Parser: 表达式解析器
|- Function: 自定义函数类
|- FuncParse: 自定义函数、求和函数解析类
|- expr (package):表达式分解类
	|- Expr: 表达式类
	|- Term: 项类
	|- Poly: 多项式类,同时也是因子类
	|- PolyKey: 多项式因子类
	|- Tri: 三角函数类

UML类图

度量分析

  • 类的复杂度分析

下面只取复杂度最高的两个类:

  • FuncParse

findRight()的基本复杂度较高,它的功能是匹配sum()和f()两种函数左右两边的括号,便于字符串的替换。实现时用栈来判断,故循环语句、判断语句导致复杂度较高

parse(String)函数用来解析并且替换原字符串的自定义函数和求和函数,导致耦合程度较高。

  • Parse解析类

parseFactor()解析表达式因子的基本复杂度较高,调用了许多函数解析不同类型的因子。

ParseTri(String)用来解析三角函数因子,为了解决三角函数内部符号以及特殊情况的优化问题,如sin(0),cos(0),sin(-x)+sin(x)等,本人在此类中对三角函数进行了一些变形,其方法有些愚笨,导致复杂度过高。

第三次作业

作业构思

由于本人第二次作业就考虑到了三角函数内部因子可能是表达式的情况,第三次作业就只是增加了了自定义函数的递归调用,并且在多项式类中对三角函数的平方和以及二倍角公式进行了优化。

UML类图

度量分析

  • 所有类的整体复杂度分析

下面对复杂度较高的几个类进行分析:

  • FuncParse:和第二次作业相类似,由于同时解析求和函数与自定义函数,方法耦合度高
  • Poly

isNeedKuoHao方法旨在判断三角函数输出时,内部因子是否需要加上括号后输出。该方法通过多次判断语句返回boolean类型,导致复杂度较高。但是为了正确输出,这是必要的。

simplifyF方法旨在化简三角函数平方和,利用多层循环语句与判断语句,导致复杂度很高。

simplifyT方法旨在化简三家函数的二倍角公式,利用多层循环语句与判断语句,导致复杂度很高。

  • Parser

与第二次作业类似,解析三角函数和因子时复杂度较高。

BUG分析

第三次作业互测时出现了Bug,原因在于本人未理解互测对sum求和函数的上下限的限制。出现的问题在FuncParse求和函数解析类中,本人对上下限采用的是int类型,但是这导致当上下限本身很大但相差不大时会报错,解决方案就是将int类型数据转化为BigInteger类型。属于是没有认真理解题意导致的Bug。

架构设计

  • 在第一次作业时,主要面对3个问题:
  1. 如何解析表达式?

参考过训练的代码后,决定采用递归下降的方法解析表达式。

  1. x如何存储?

和同学们讨论之后,采用了HashMap存储x的指数和系数的方法,利用HashMap键的不可重复性,可以简便地对表达式进行化简。当时计划着要写多个因子类,但是具体实现时发现只需要一个多项式类就可以存储所有类型的因子,常数因子的指数为0,系数即为常数;表达式因子可以先转化为表达式,然后利用表达式的makePoly方法转化为Poly,故没有写过多的因子类。

  1. 多符号问题如何解决?

和同学讨论之后,决定在表达式层建立一个符号数组,对项与项之间的+,-进行存储;在项层标明符号;在因子层符号即为系数的符号,同时优先顺序为表达式>项>因子,缺省默认+。例如,对+-+1这样的数据进行如下处理,从左到右,第一个+是表达式层数组的一个元素,-是该项的符号,+1则是因子层的系数,当然,如果出现-1这种情况,-号为表达式层的符号,项层和因子层默认为正号。这样处理的情况下,需要在Expr类调用makePoly方法时考虑符号问题,对不同的项之间进行加减运算。

  • 在第二次作业时,主要考虑3个问题:
  1. 增加了三角函数之后,如何存储并能合并三角函数同类项?

要想快速方便的合并,就需要HashSet或HashMap。本人考虑之后,决定对原Poly类中的HashMap进行修改,value仍为项的系数,但是为key专门建一个类,该类存放x的指数,以及三角函数的HashMap,key对应的是三角函数类,标志了三角函数的类型和因子,value为三角函数的指数,便于合并同类项。

  1. 什么时候处理自定义函数和求和函数?

本人考虑了在解析表达式的同时去解析自定义函数和求和函数这种做法,但这种做法需要用到深克隆,本人不太熟悉又担心出错,而且第一次作业采用的递归下降的做法支持括号的嵌套,因此直接采用了解析表达式前对输入字符串进行预处理,替换字符串的自定义函数和求和函数部分。

  1. 如何处理自定义函数?

本人考虑到第三次作业有可能不再限制自定义函数名和变量数量、名称,故为自定义函数专门建立了一个Function类,存储自定义函数名称, 自定义函数形式参数,自定义函数表达式。该类被FuncParse类使用,进行自定义函数的替换

  • 在第三次作业时,主要考虑了3个问题:
  1. 如何输出三角函数内部因子是表达式因子的情况?

由于三角函数类中存储因子的形式就是Poly多项式类,故在多项式类中设置一个标志数据,标志该对象是否为三角函数的因子,并结合其他条件在本类的toString方法中进行输出。

  1. 如何对三角函数进行化简?

本人化简了二倍角公式和三角函数平方和,不断地对Poly类中的map进行二重遍历,找到满足合并条件的两个项进行合并,直到不存在能合并的两个项为止。

Bug分析

本人在本单元作业中,未对任何人进行hack,但就对如何发现自己的程序的bug进行分析:

首先要保证基础数据的正确性,可以构造一些简单的随机的数据测试。

其次对优化性能的部分进行测试,平方和、二倍角、x**2变为x*x等优化,要做足测试。

最后测试一些极端的数据,比如大数、三角函数中嵌套多层括号因子。

心得体会

本单元第一次作业难度较大,本人以前从未接触过面向对象思想,导致第一次作业做的时候没有任何头绪,难以下笔。后来看到训练的递归下降的做法,有了一点头绪。在与同学讨论的过程中逐步有了整体的思路。第一次作业到第二次作业以及第三次作业的过渡都比较顺利。经过了这3次作业,对Java语法更加熟悉了,认识并且会使用递归下降方法了,并且对于分层次解决问题以及面向对象的思维有了更深的理解。不过OO教会我最大的东西是,如果对一个问题没有思路,先弄清楚哪里卡住了的问题,再把问题写下来,逐个攻破,而不是坐牢一样的瞎想。

posted @ 2022-03-23 18:29  KouweiLee  阅读(70)  评论(1编辑  收藏  举报