2024面向对象设计与构造第一单元博客

张学东-22373307 学生 2024-03-23 19:07:59

面向对象编程第一单元博客作业

总体来说第一单元作业给了我很多的教训和经验

基于的度量代码分析

对于最终第三次作业类的复杂度分析如下:

ClassOCavgOCmaxWMC
Definer448
ExpFactor1.67310
Expr2.5420
FuncFactor116
InputProcess2.5610
MainClass111
Mono21132
Number117
OutputProcess1.2525
Parser4.251134
Poly3.44855
Term2.22520
VariableFactor1.539
inputprocess.Lexer1.73719

我们可以看到Parser、Poly、Mono的复杂度较高。

Parser是因为在解析表达式时加入了很多不必要的特判,在作业迭代之间没有充分提取作业之间的共性,从而导致很多细节上的处理需要用if-else特殊处理。

Poly类作为表达式运算的主体,承载了加减乘法的集成,同时也有合并同类项的功能,导致代码臃肿

Mono作为Poly中的基本单元,承载了Poly的运算特性,代码复杂度较高。

整体来说,因为没有设计单独的优化单元,使得优化的内容杂糅到Poly和Mono类中去了,然后在递归的过程中在Pareser使用了特判,这是这几次作业产生bug的根源。

架构设计

类图与每个类的说明:

img


从此次作业的类图中可以看出,第一单元作业在设计框架时有些混乱,尤其是Parser类中

以及Mono类的拆分不够细致,导致方法多且十分臃肿

  • MainClass:这是代码的主类,生成Inputprocess获得并简化输入,构造Lexer类,到构造Parser类解析,并生成Poly通过OutputProcess输出。
  • InputProcess:这是获取输入的类,通过Scanner获得输入,方法getStdinput删除空白符,方法OpSimper是用来化简加减法相连的情景
  • Definer:这是用于函数解析的工具类
  • Lexer:用于递归下降中解析token的类
  • Parser:用于进行递归下降,生成语法树的类
  • Factor:这是因子接口,是语法树每个节点的基本单元,其下有VariableFactor用于幂函数 Number用于纯数字 FuncFactor用于函数 ExpFactor用于指数函数 Term是包含多个Factor的类 Expr是包含多个Term的类 Term对应单项式 而 Expr对应多项式
  • Mono:作为输出的基本单项式
  • Poly:作为多项式,包含多个单项式构成。存储Mono的方式为HashMap<power,Hashmap<Poly,Mono>>,power是幂函数的指数,Poly是exp指数函数内部的多项式子,ExpFactor内部Poly的递归终点是空的Poly。通过hashmap方便在合并同类项时能够快速捕捉能够合并同类项的两个单项式。

一些关键的方法:

  • toPoly:把每个因子转化为多项式的过程,如在term的toPoly的方法是把其内部的Factor全部变成Mono后加入到一个新构造的Poly中。这样便于后续的计算。
  • addPoly:在这个类中,我整合了自动合并同类项,枚举遍历所有的Mono时,构造一个新的Poly,如果poly中没有这个mono直接加进去,如果已经有对应power和Poly的mono,那么把这两个Mono相加后存入
  • callDefFunc:这个是在解析输入的时候,存储形参和实际对应的String
  • addDefFunc:在解析时,把函数因子替换为真实的表达式

一个比较失败的设计:在解析dx时,直接把dx内部当成Expr解析了,写法臃肿,如果建立一个DxFactor就会使代码复杂度下降很多。

迭代过程:

HW1:完成作业的大体框架,输入输出的处理以及语法树建立和多项式处理的内容

HW2:增加函数,新建Definer和FuncFactor、ExpFactor等函数和指数函数类

HW3:新建dxParser方法,在每个Factor增加求导方法

扩展性:

本次设计的代码有一定的扩展性,比如说我在解析时没有默认输入的未知数一定是x,但是如果要加入多变量的情况,我需要考虑在addMono方法增加判断两个未知数字母是否相同。
同时,要改写Poly中存储Mono的hashmap,增加一层未知数字母的嵌套。

BUG分析

HW1

hw1的bug出现原因是代码架构层次混乱,一些没有提前想好的地方直接就上手写了

问题在于我在判断Mono单项式是否为常数的时候,单独设置了一个为num的变量和isConst的布尔变量,其实只需要通过判断指数Power是否为0就可以判断这一项是否为常数,根据前面度量分析也可以看出来,这一部分Mono的代码复杂度高,引用混乱,导致bug出现

HW2

hw2的bug出现了多处:

  • power的存储使用了int型变量而非Biginteger类,导致了在括号迭代时发生了数据溢出
  • Parser解析exp(时会在toString里多输出一个(,也对应了前面Pareser类特判多,复杂度高的问题
  • 使用hashmap的contain时,只重写了Poly的hashcode,没有重写Mono的hashcode,这部分是对hashmap的hashcode底层原理理解不够清晰导致的

HW3

Hw3的bug均为TLE

是由于在进行加法运算时,每次需要进行加法运算,导致迭代产生多次hashcode的计算,用一个变量存储算过了的hashcode避免重复计算。

互测策略

在互测中,能够hack成功的样例点,有一些共性。

比如在第二次作业中,我通过0\n 1-1 这组数据hack掉了1个人,

通过下面的数据hack了一个人

1 g(y)=+y*2*exp(y)^+7 g(x)

在构造样例点的情况,考虑了以下的样例构造方式:

  1. 首先,构造的样例要有目的性,比如 g(y)=+y*2*exp(y)^+7 这个代码就是为了,hack解析时符号出现混乱的情况,因为我自己在写代码的时候自己就出现了这样的bug
  2. 构造的样例考虑了极端性exp((188167972192200*x+282251958288300*exp(x^2)))
  3. 采用测评机随机生成样例然后对拍

分析优化

本次作业中我使用的优化方式是,在OutputProcessor中对要输出的字符串进行化简,化简的思路就是合并同类项,删去0,并合并简单的加减法连续情况,删去expr的前导0和前面的加号。

这样的优化方式,虽然在每次作业过后不会对正确性产生影响,优化的效果有限。

因为单独设置了输出处理的类,确保了代码的简洁性,我的代码也因此没有在这方面产生任何bug。

心得体会

第一单元的面对对象设计与构造课程,对我来说挑战性和教训还是不小的。主要在以下几个方面

  1. Java的一些特性不熟悉,比如说第二次作业前面分析的bug修复中的hashmap出现的问题,就是因为对Java中hashmap的特性不熟练
  2. 作业设计太匆忙,急于求成,没有提前设计好就上手了,造成后续会产生一些自己难以发现的bug和层次混乱
  3. 在程序的设计时不要为了求稳只考虑正确性,如果性能设计出现了问题,就算没有死循环,也会非常地慢,导致程序出错

课程建议未来方向

增加关于java语法层面的一些教学内容,这方面的内容放在oopre里面,但是还是十分破碎,导致很多小细节没有系统地讲清楚,在debug环节这些问题都会一个接着一个地出现。

天下大事,必作于细。希望课程组能够增加课程精度,增加可以提供参考的学习资料,只是通过搜索、讨论区或者博客来学习一些自己没有了解过的概念,有一定的帮助,但是效率还是比较低的,难以在短时间内系统掌握相关知识。

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

301

社区成员

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

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