OO第一单元总结

OO第一单元总结博客

一、程序结构分析

1. 第一次作业

  • 思路分析

    第一次作业需要实现简单的单变量多项式括号的展开,考虑到这次作业的BNF描述相对简单,而且加入了一定的数据限制,所以在递归下降解析和手动解析之前我选择了后者。针对表达式、项和因子分别递推出相应的正则表达式进行解析并分别为止构建相应的类和方法。

    尽管顺利完成了第一次作业并且在公测中也取得了不错的成绩,但第一次的架构设计和解析方法为后面的作业埋下了不少隐患。

  • 结构分析

    UML图

可以看出在第一次作业中并没有定义太多的类,类与类之间的关系也并不复杂。核心思路是在处理输入表达式的时候,借助正则表达式不断解析出一个个项,调用每个项的calCoef()方法从项中解析出因子并记录因子的系数和幂次。之后再调用表达式的calCoef()方法合并存储在表达式中所有项,得到最终的结果。

  • 度量分析

可以看出,CogC较高的几个方法分别是Term.calCoef()方法和toString()方法,这也印证了我的设计思路,整个程序的计算部分主要由Term类实现,最后的输出部分由各个类的toString()方法实现。同样的,可以看出calCoef()方法的iv(G)、ev(G)也相对较高,在代码中体现为这个方法的实现太过冗长,与其它模块的耦合度太高,在checkstyle阶段导致了扣分。

  • 优点:架构的结构比较清晰,实现比较简单,类与类之前没有复杂的关联关系。
  • 缺点
    • 没能充分利用OO思想,类内部定义了一些冗余的属性,没能很好的抽象出Factor这个父类。
    • 部分方法的实现太过复杂,仍然受到了面向过程设计思想的影响,体现在设计了Utils工具类来辅助程序实现功能。
  • bug分析
    • 第一次作业的中测和强测都顺利通过,但是在互测阶段被找到了一处bug,是因为在递推正则表达式时出现了纰漏,一是由于没有仔细阅读指导书中的形式化描述,二是由于在写大正则时出现了细节问题。在bug修复后我也没有很重视这个问题,导致问题在HW2中集中爆发。
    • 在互测阶段也阅读了其他同学的代码,被查出的bug主要集中在边界的处理和化简过程。

2. 第二次作业

  • 思路分析

    尽管第二次作业加入了三角因子、求和函数、自定义函数等新的数据要素和括号嵌套,但我发现在对输入数据进行限制后依然能通过正则表达式对因子、项和表达式进行建模,因此我沿用了第一次作业的架构。

    第二次作业的难点主要在三角因子和函数,引入三角因子之后合并同类项和化简的难度增加了不少,在这里我的处理方法是在项中重新构造一个MyComparator类,针对因子的类型自定义项中因子的排列顺序,便于化简。

    对于自定义函数,我使用了字符串替换的方法,将实参整体带入到函数定义中,为了防止参数和自定义函数定义中的符号引发冲突,为每个实参加入括号之后再进行带入。

    对于求和函数,通过for循环针对迭代因子构造出一个新的表达式因子带入到原项中。

  • 结构分析

    UML图

虽然第二次作业在第一次作业的基础上扩充了一些类和方法,但总体思路并没有很大改变,依然是通过正则表达式对表达式、项、因子分别进行建模,在Term中使用HashMap<String, BigInteger>进行因子的合并简化处理,在Expr中使用HashMap<HashMap<String, BigInteger>, BigInteger>进行项的合并简化处理。尽管已经通过项内排序处理了一部分合并问题,但在Expr层的项的形式过于复杂,没有能想到更进一步的优化步骤,导致最终的性能得分不是很理想。

  • 度量分析

同第一次作业一样,CogC较高的几个方法大多数都与层次分解、简化合并有关。由于本次作业数据复杂度进一步提高,模块间的耦合性也相对提高,通过度量分析已经能够看出该设计架构存在很大的弊端。

  • 缺点
    • 正则表达式过于复杂,不仅容易引发错误,而且严重影响程序性能
    • 不能够很好的处理简化合并问题
  • bug分析
    • 第二次作业在强测阶段失去了很多分数,问题主要发生在正则表达式的递推。在本次作业中需要定义大量的正则表达式,大正则的匹配速度很慢而且表达式因子的形式十分复杂,括号、符号的匹配稍不留意就会发生错误,这些错误在调试、bug修复阶段耗费了我大量的精力
    • 在互测阶段并没有被查出bug,可能的原因是正则表达式的问题不容易通过人工肉眼观察来发现,这也证明了通过正则表达式手动解析表达式这种方法存在很大的弊端。

3. 第三次作业

  • 思路分析

第三次作业加入了三角函数和函数的嵌套,同时减少了对输入数据的条件限制,这样一来之前作业架构的局限性被无限放大。最开始我试图在之前的架构上进行迭代,首先遇到的问题就是需要写臃肿的正则表达式,第三次作业项、表达式因子正则表达式的形式都十分复杂。在还不能保证正确性的前提下,我对整个程序进行了调试,发现程序在大量的正则表达式匹配和层次分解中运行的十分缓慢,在某些括号嵌套和反复递归的过程中出现了始终无法解决的bug,因此无奈选择了重构。考虑到重新学习递归下降方法可能会来不及提交作业,因此选择了预解析输入建立表达式树进行处理,思路比较简单,这里不再赘述。

  • 结构分析

    UML图

预解析建立表达式树的解题思路比较固定,结构比较简单。可以看出类与类之间的关系简单明了,最终通过调用表达式树树根的toString方法并进行一些展开化简操作得到结果。

  • 度量分析

第三次作业架构的复杂度相对第一、二次下降了不少,复杂度主要体现在与最终展开、化简有关的类与方法中。

  • 优点
    • 架构简单、结构清晰、只要逻辑正确不易发生Bug
  • 缺点
    • 使用了预解析输入,损失了作业得分
    • 输出结果的优化和化简效果不太好
  • bug分析
    • 在互测阶段未被找到bug,但是在强测阶段出现了RE,原因是在使用HashMap的时候出现了问题,导致本地测试结果与评测机结果不一致

二、Debug总结

个人Debug体会

在第一单元的作业中我没有写数据生成器来进行样例覆盖,主要通过分析BNF描述和数据限制来手动构造一些可能会出现问题的数据,主要发现了以下几类Bug

  • 爆long:这个应该算是最基础的一类bug,在第一次作业中被发现
  • 正则表达式书写错误:这个问题在被发现之后并没有引起我的重视,导致后期大量在此失分乃至架构的重构
  • 空串、0,-1、1等边界bug:这类问题往往是由于过度优化导致的

互测Debug体会

在第一单元的互测中主要通过一些边界数据、往期作业的强测、互测样例、自己出现bug的一些数据对其他同学进行hack,发现问题主要发生在以下几个方面

  • 过度优化:一些暴力的字符串替换优化,如把x**1强行替换为x,这样与x**11有关的数据就会发生问题
  • 边界数据:不少性能优化做的很好的同学在例如-1,0等数据上出现了纰漏,导致失分
  • 符号处理:一些同学在对项和因子进行处理时会事先处理好整个式子的符号,但没有考虑到指数的符号

三、心得体会

  • 第一单元给我最深刻的教训就是一定要设计一个好的程序架构以及重视training训练,不然后面每次作业都会带上痛苦面具。在第一次作业中我仅仅是为了能够通过公测以及获得优化分而进行设计,导致可扩展性很差,之后每次作业都花了很多的时间去改进和重构。
  • 发现自己还没能跳脱出面向过程程序设计的思维,仍然会定义一些冗长的方法和类以及使用大量if else的嵌套,导致架构十分臃肿。
  • 在看到第一次作业的时候发现形式和难度都跟pre不是一个量级,这也导致我花费了很长的时间去适应,好在在一次次试错和无数次被强测互测干烂之后摸清了OO的一些套路。每次完成作业之后在长舒一口气的同时也能获得不少的成就感,希望在第一单元的教训之上以后的OO之路能顺利一些(
posted @ 2022-03-25 12:32  Tsundokku  阅读(55)  评论(0编辑  收藏  举报