BUAA OO第一次作业总结

郭俊杰 -22371240 学生 2024-03-23 17:02:03

量化分析

类的属性个数、方法个数、代码规模。

类名属性个数方法个数代码规模
Mainclass0022
Token2229
Lexer3479
parser14132
Expr113171
Term519188
Factor004
NumFactor1740
VariFactor1632
ExprFactor2135
ExpoFactor1653
DeFactor1112
Function3224
FuncFactor2365

每个方法规模、每个方法的控制分支数目

方法名/所属类方法规模方法控制的分支数目
Mainclass类  
main110
Token类  
getType10
getContent10
Lexer类

 

 

getNum18

2

next10
getToken10
notEnd1

0

Parser类

  
ParseExpr285
ParseTerm203
parserFactor4711
getNum133
getTerms10
isSimple81
equals112
addTerms10
sort202
add60
mul60
simplify101
diffFunction70
diff10
SimToString4011
toString10
Term类  
isNum10
isVari10
isExpo10
isFactor10
getNumFactor10
getVariFactor10
getExpoFactor10
isSimple10
isZero10
isSimilar20
equals30
isPos10
addFactor115
simplify182
diff80
mul60
add50
compareTo10
toString287

NumFactor类

  
toString62
equals10
getNum10
mul10
add10
isOne10
VariFactor类  
getIndex10
mul10
isConstant10
toString62
equals10
ExprFactor类  
simplify152
ExpoFactor类  
isConstant10
equals10
getExpr10
isSimple10
mul10
toString52

DeFactor类

  
simplify10
Function类  
getExpression10
getParas10
FuncFactor类  
parseFuncs111
simplify100
replace232

复杂度分析

可以看到Lexer类和Parser类的平均复杂度圈较高,因为在Lexer类中需要对输入的表达式进行处理将其转化成Token序列,Parser类对Token序列进行递归下降时需要判断因子的具体类型,都涉及到大量的if判断导致复杂度较高。

而Expr与Term类中包含许多对表达式进行运算和化简的函数,因此总复杂度较高。

架构设计

hw1

UML类图:

Token类定义了表达式中最小词元。

Type类为Token类的内部类,用于记录该词元的类型。

Lexer类用于记录由最小词元组成的表达式,并提供遍历的方法。

Parser类用于解析Lexer对象,生成Expr对象。在解析表达式时使用递归下降法,定义Expr类的属性为Term对象的一个序列,Term属性为Factor对象。在解析表达式即parserExpr函数中,根据"+\-"号将表达式分解为项并调用parserTerm函数解析。在解析项即parserTerm函数中,根据“*”号将其分解为因子并调用parserFactor函数解析。

Factor为接口,给出了字符串转化和化简的抽象方法。VariFactor, NumFactor, ExprFactor三个类实现了Factor接口,内部定义了相应的运算和化简方法。

在第一次作业中需要将表达式最终化简为一个关于x的多项式。对于最后化简的结果,每一个基本项都可以表示为一个常数因子与一个幂函数因子的乘积,即形如"a*x^b"的形式。除了这两个因子,一个项还可能包含表达式因子。在将输入的表达式读取完成后再对其进行化简,将表达式中每个含有表达式因子的项化简为多个基本项,从而实现了去括号。

hw2

UML类图:

第二次作业新增了以下类:

Function类用于记录自定义函数。

FuncFactor类用于对输入表达式中出现的自定义函数进行处理。

ExpoFactor类用于表示指数函数因子。

在第二次作业中引入了自定义函数与指数函数。对于自定义函数我选择在预处理时进行字符串的替换。由于引入了新的指数函数,此时的基本项变为"a*x^b*exp(<expr>)"的形式,其中expr为化简后的表达式。在Term类中增加新的属性ExpoFactor对象。在表达式化简时,需要对表达式的每一项化简,而化简每一项时先对指数函数因子中表示式进行化简,通过此过程递归地实现了化简表达式。

hw3

UML类图:

 第三次作业新增了DeFactor类,用于表示求导因子。

在第三次作业中引入了求导因子。本次作业的基本项和第二次作业相同。在Term类中增加新的属性DeFactor对象,在化简表达式时将求导因子求导后化为表达式添加到所属Term对象中,问题就变成了第二次作业中的情况。而在对求导因子进行求导操作时首先要将其中表达式中每一项的求导因子进行求导操作,于是通过此过程递归地实现了对求导因子的求导操作。

新迭代情景

在之后的迭代中可能会引入新的因子,如三角函数因子等。在进行迭代时只需要完成以下工作:

  • 增加新的因子类并实现Factor接口。
  • 扩展基本项的形式。
  • 在Parser类的解析因子的函数中增加新的分支解析并返回新的因子类的对象。

程序bug分析

在第二次强测中出现了两个bug:

1.一开始对于自定义函数的处理中我新建了一个自定义函数类并添加到Term类的属性中,在化简时将自定义函数表达式中的形参替换为实参后以替换后的字符串作为新的表达式对其进行解析,最总得到一个不含有自定义函数的表达式因子将其添加回Term对象中。这种方法由于复杂度太高,时间与空间的开销都比较大导致几个数据出现了TLE和RE。

2.最初在自定义函数替换的过程中直接在函数表达式中查找形参的位置并替换为实参,导致忽略了这种情况:

1
f(y,x)=x+2*y
f(x,x*2)

此时将y替换为x后再进行x的替换时会将刚刚由形参y替换为的x当作形参,从而导致错误的结果。

因此我对自定义函数表达式进行了预处理,将其中所有形参后面加上该函数名,从而在查找能够区分实参与形参。

互测分析

在互测中先使用自己在debug时出现问题的数据进行测试,同时可以构造一些测试边界情况的数据,再使用测评机进行测试。

程序优化

1.在表达式化简后输出前进行同类项合并,将系数为0的项去除,并对所有项按照幂函数的指数从大到小排序。在输出时首先搜索项中系数大于0的项将其输出,这样使得在输出为“1-x”这种情况时使得表达式的长度更短。

2.在预处理中从后往前替换自定义函数因子,从而实现了对函数嵌套情况的处理。

3.对自定义函数表达式进行预处理,将其中的形参前加上占位符,保证了后续的替换不会出错。

心得体会

这三次作业总体难度适中,在设计的过程中我关注到了一些之前忽略的点,比如构造函数中对象的赋值问题,如果不进行克隆直接赋值可能会引发一些问题。一个方法中是选择返回一个新的对象还是直接改变该对象的属性而不返回等。在最初写项与表达式计算的方式时我选择了直接改变对象的属性而不返回,后来发现在表达式相乘时,表达式中每一项会进行多次运算,这种写法会使得项在第一次运算时就被改变,而导致后续运算出错。通过完成作业,我更深刻的理解了这些问题以及不同情况下选择哪种方法更合适。

同时我还认识到一个好的架构的重要性,一个好的架构可以提高代码的可读性,可维护性,同时在后续迭代中的只需添加新增的功能而不用大幅修改原来的代码,极大地节约了时间。

未来方向

在课程设计上未来的作业中可以为同学们多提供一些框架,以让同学们更好地体会到好的框架在后续迭代过程中的重要性。

...全文
52 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

301

社区成员

发帖
与我相关
我的任务
社区描述
2023年北航面向对象设计与构造
学习 高校
社区管理员
  • YannaZhang
  • CajZella
  • C_ecelia
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧