北航2020年OO第一单元总结

题外话

​ 素闻 6 系 OO 费心劳神,极为恐怖,但程设未到平均分的本菜鸡无知无畏,我在寒假的时候试图通过直接阅读指导书来步入 OO 殿堂,于是就有了下面这一幕:

​ OK,认清现实,从零开始,针对 pre 中的需求,在网上找一些资料踏踏实实地来学习。经过几天的“自我扫盲”,起码算是入了门,知道了类和对象大概是怎么一回事,也初步了解了正则表达式和一些简单容器的使用,顺利通过了 pre。

闲话结束,下面进入正题。

1. 第一次作业

1.1 思路分析

第一次作业相对友好,仅仅需要针对幂函数这一种主体因子完成求导。本人的思路如下:

  • 由于第一次作业没有 wf 判断,先无脑去空白字符;
  • 根据幂函数特性编写相应的正则表达式,并调用Matcher类的find方法来分割字符串;
  • 分析字符串结构,完成幂函数的构造;
  • 简单求导;
  • 将指数和系数作为键值对来合并同类项,去除多余符号,同时提取系数最大项至最前,完成化简。

1.2 结构分析

UML 图如下:

由于本人仍旧保留着严重的面向过程思想,只是新建了一个 Term 类,类中仅有一个上述的构造函数以及指数、系数属性,其余绝大多数工作,包括字符串的预处理,求导,乃至化简都是在主函数中完成,缺少 OO 思想和层次感,“一 main 到底”不忍直视;
复杂度分析如下:



可以看到,虽说实现的功能较为简单,但不善于利用模块化、结构化思想依然会使程序复杂度飙高。

1.3 Bug 分析

  • 由于代码量相对较少,整体结构并不复杂,第一次作业我并未搭建评测机,而是手工构建出一些极端数据,修复了一些手抖产生的 bug,并且并未在强测和互测中被hack;
  • 人工检测 7 个人的代码,多少有点费力,好在最后还算有收获,发现某位兄弟遗漏了化简后应当输出零的情况,他的程序并未输出。

1.4 感想与收获

  • 新手上路,还是习惯在舒适区里写大块代码,为日后的重构埋下隐患;
  • 对正则表达式加深了理解,用起来更加灵活;
  • 学会在实践中学习,写代码 →发现需求 → 查阅资料 → 使用容器、调用函数等解决问题 → 继续写代码;
  • 惊叹于别人优秀的代码,学到了很多,其中我第二次作业还借鉴了某位 roommate 在第一次作业中整体结构。

2. 第二次作业

2.1 思路分析

第二次作业难度提升不少,又是 wf 又是三角函数又是乘积的,乍一看无从下手。一番仔细思考之后,有了自己的思路:

  • 先依据指导书中的形式化定义一步步写出正则表达式,并且封装在一个枚举类中,随用随调;
  • 去空白字符后,先整体判断是否合法;
  • 创建以x的系数,sin的系数,cos的系数这一三元组为键的HashMap,和第一次一样按项读入,不过提前合并;
  • 根据乘法求导法则完成求导;
  • 优化,将x**x化为x*x,a*sin**2+b*cos**2化为(a-b)*sin**2+b,cos**(i-2)*sin**jcos**i*sin**(j-2)合并。

2.2 结构分析

UML 图如下:

第二次作业,逐渐有了面向对象的思想,针对幂函数、sin 函数、cos 函数分别构建类,并在类中实现求导方法,再往上是之前提到的三元组类 KeySet (名字取得不太好)以及项的类(叫 ThreeTerm 还是感觉有点奇怪),然后是多项式 poly 类,最后是实现化简的两个类,main 中仅仅完成一些事前和事后输出工作,结构还算清晰;
复杂度分析如下:



我得承认,在处理三元组以及最后化简时,我为了图省事把很多操作塞到一个方法里,导致相应数据飘红,不过整体来看,结合代码行数考量,和第一次相比在结构化方面有所进步。

2.3 bug 分析

  • 第一次研讨课的时候,听着大佬们分享各式各样的评测机写法,我听得云里雾里,深感惭愧,因为我对此可以说是一窍不通,学 Python 嚷嚷了几万次,每次都无果而终。终于,纯手工造数据已无法满足自测的需求(尤其是我还优化了),现实逼迫我再怎么着也得弄出一个评测机来。于是,我完全仿照讨论区中 qsy 大佬的思路,结合我少得可怜的 Python & Linux 知识,不懂就百度,花了一整天时间写出了一个评测机。效果还是有的,找到了我两个直接向map中插入键值对导致原值被覆盖的问题。但悲哀的是,我忽视了“二八原则”,在FewPolish中,另有一处同样的 bug 需要很巧合的数据才能发现(还是评测机太弱了),强测无事,互测直接 GG。(见下图,仅截取部分,原代码中未加 if 判断语句)
  • 这一次的互测我先大体扫了一遍代码,然后直接丢进评测机。虽说找到了别人三个符号连一起就输出混乱的错误,但是稍显遗憾的是,一位老兄指数等于 10000 时报错,另一位老兄前导零过多时出错,评测机无法得到对应数据,而我自己针对性地构造是有可能 hack 成功的。

2.4 感想与收获

  • 代码架构真的很重要!由于第一次作业毫无扩展性可言,我第二次作业只能完完全全地重构,好在这一次作业面向对象的思想也有所体现,自底向上一步步完成编写,为第三次作业也贡献了不少代码;
  • 辩证地看待代码优化和评测机。本人写评测机花了一整天,代码优化又花了一整天,结果性能分几乎没涨,还引进一个评测机没发现的 bug(归根结底菜是原罪,写的评测机过于简陋以致无法产生特定数据,优化也仅仅停留在表面,没法像那些大佬一样递归优化到极致)。但这两天收获颇丰,逼着自己跳出舒适区学一点不熟悉的东西,bug 的产生让我对 Java 的特性又有了新的理解。
  • 面向过程的痕迹依然存在,但罗马不是一天建成的,一步一个脚印地领悟 OO 精髓;

3. 第三次作业

3.1 思路分析

压轴好戏名不虚传,嵌套求导差点把我吓傻,曾经一度觉得自己没法在 ddl 前理清思路。讨论区看了个遍,什么有限状态机,递归下降直接劝退(还是菜啊),百度搜了个遍,大脑依然空空如也,没办法,回来钻研指导书,发现文末有一些微妙的提醒,遂慢慢有了想法:

  • 先针对其他字符的 wf,空白字符位置的 wf 进行判断,而后去空白字符;
  • 为完成嵌套,将多项式看成因子,按照 polytermfactor 递归解析,直至遇到仅由幂函数或者常数构成的项(我在这里将三角函数看做上述的嵌套);
  • 采用工厂模式,根据传入的字符串特征进行解析;
  • 逐层求导,将返回值以字符串形式返回至上一层;
  • 去零项,简单化简。

3.2 结构分析

UML 图如下:

本课程所需的设计思想逐渐有所体现,每一种函数、项以及多项式的类中完成各自特定方法,另有工厂类、异常类、求导法则类各司其职,“高内聚,低耦合”初见端倪;
复杂度分析如下:


好嘛,红数据又辣么多。回看代码发现,由于上下层间过于紧密,导致模块的耦合度偏高,模块设计复杂度等指标自然会飙升。这也启发我在日后写代码时要更加注意层次间关系的梳理,模块间的调用尽量清晰简洁,避免无谓的 debug。

3.3 bug 分析

  • 由于第三次作业理出思路加上写代码确实占了太多时间,再加上我并未做过多化简以及这次的评测机确实不好写,我参考了一位学长的博客,简单进行了评测。还是同样的问题,出的 bug 是评测机兼顾不到的,强测中(- 1)的 wf 没有特判出现 bug;ddl 前的一个小时看到群里在讨论 tle 的事,自己试了试发现果然中招,慌忙捕虫但却回天乏术。不出所料,互测中招,tostring 方法递归过深导致超时,以及去零的化简出现漏洞导致类似 sin(1)+x 这样的求导直接变成 cos(1)+1,互测中被 hack 了 13 下,甚至有个哥们交了很多次类似的卡我递归的数据,有点不爽;
  • 吸取了上次的教训,我这次采用手工为主,评测为辅的方法进行找 bug。可别人也吸取教训了呀,边界条件出错一个没有,化简出错一个没有,递归过深还是一个没有(倒是自己玩砸了),遂无奈,将目光放在代码上,发现一化简极佳的兄弟处理 (sin(x)+sin(x))sin(1) 这样的数据时会出现 cos(x)+cos(x))sin(1 的括号匹配的错误,另找出一位兄弟一处低级错误,不需多说。

3.4 感想与收获

  • 设计模式真香!继 pre 工厂模式小试牛刀后,这一次正儿八经地用工厂模式来处理数据,对多态这一特点理解更深了,但学无止境,有用的创建模式、设计模式还需花时间钻研;
  • 折服于别人化整为零的框架结构以及精妙的化简技巧,潜移默化中能力有所提升;
  • 类中的方法从臃肿变得更加精简,结构层次逐渐清晰;
  • 以自己的惨痛经历对程序鲁棒性有了不一样的体会。

4. 总结与展望

第一单元悄然结束,整体来看,虽完成得不算多好,但还算差强人意。更重要的是,感谢老师和助教的细心引导,让我就此打开了面向对象的大门,门内五彩缤纷的世界还需要我静下心来欣赏。希望第二单元从一开始就要把模块化、可扩展的思想落到实处,可别再像第一单元那样让重构成为常态了;希望日后的代码之路能越来越顺畅,学到真正实用的知识,让“码力”内化成自身重要的竞争力。
而从本课程或许又能看到人性百态。讨论区内友好相助,ddl 前疯狂至近乎绝望,互测屋里“勾心斗角”,“内卷大系”所有人为了一两分的斤斤计较,可谓淋漓尽致。
以上一段纯属胡诌,第一次写博客,如有不当之处,还望拨冗指正。

原文地址:https://www.cnblogs.com/bjhkhtdx/p/12521508.html