oo 第一单元总结
程序分析
homework_1
第一周的任务相对来说比较简单(当然是现在看来)。只涉及到了最简单的幂函数求导。当时对我来说最大的难点在于正则表达式的使用,我上网学习了很多正则表达式的基本语法,以及功能强大的Pattern类以及Matcher类。我的绝大多数正则表达式相关知识是在下面这个网站学习的,我觉得讲的很清晰。
https://www.runoob.com/java/java-regular-expressions.html
第一次作业的UML图如下
度量分析如下
(对比他人复杂度发现自己还是高了许多)
这次代码总共用了三个类,主类负责输入字符串,Extract类把读入的字符串提取成一个个项。然后在Term中生成对应多项式,并带有求导方法和toString方法。最后把结果在Main内输出。
作业反思
本次作业我写的很快,期间遇到的bug也不多,因而之后也做了十足优化,在强侧中基本得了满分,互测中也没有被hack。一时之间我觉得不过如此,但现在反过头来看这个代码,还是有很多问题:
-
无用方法太多,受到checkstyle限制,类变量全部定义为private,然后我为了图方便给每个变量都设置了get方法和set方法。现在看来很蠢(那时候连右侧的warning都不会看)。给每个变量设置get、set方法private不就毫无意义了吗?
-
主类写的冗杂,我的main类里做了很多化简工作,实际是不应该的。
-
可扩展性太差,当时写代码的时候根本没有想过后面作业的迭代。(为后面作业埋下祸根/手动狗头)
homework2
第二周的作业对我来说感觉一下子上了一个难度。我感觉有以下两个难点
- 首先难住我的就是wf的判断。愚蠢的我写不出来完整的正则表达式。
- 乘积形式的求导。
两个问题解决如下:
- 首先第一个问题确实是我脑子不好使,这次作业的表达式的确可以用一个大正则表示出来。其实只要把每个因子的基本形式写出来,再用一个类似于
private String item =
factor + "(" + whitespace + "\\*" + whitespace + factor + ")*";
这里的()*就是类似递归的形式,表达式的正则与之类似。
- 第二个问题仔细想想其实也不难,在这次作业的限定下,每个项最多有三种因子(幂函数,sin,cos),我们只需要把每个项化简为这三种基本因子的乘积形式,每个因子只需要包括类型,指数即可。求导的时候按项求导,每个项求导出来三部分,最终一起合并。
第二次作业我只做了最基础的优化,例如忽略*1因子,忽略+0项。后来在学习其他人的代码之后,测试后下来自己手动尝试了cos(x)**2+sin(x)**2
、1-sin(x)**2
和1-cos(x)**2
三种最基本的合并。
UML图如下
度量分析如下
这次作业基本读取字符串部分除了修改正则,基本沿用了第一部分作业。为了区别三种不同的因子新加了因子类,求导处理与作业1类似。算是最大程度化利用了第一次作业。我以为解决了一开始的两个问题就可以高枕无忧了。但在实际做的过程中,发现各种符号是一个大问题。最终选择给项加了一个符号变量,最后在输出的时候先输出符号再输出具体内容。
在互测的过程中,自信满满的我被hack无数次,我隐隐的感觉到这次要崩。果然强测完全崩掉了。。。
bug原因
由于每个项都使用了一个符号变量,我在最后输出的时候先输出了这一项的符号,再输出这项的求导内容。但是实际上求导后,每一项会变成三项,这三项是一个整体。(相当于带一个括号) 但在我这样的处理下相当于只给第一项分配了符号,而忽略了后面的两项。
这个bug可以称为是毁灭性的,我的强测只过了六个点。这个错明明很容易被de出来啊啊啊啊啊!为什么我没有找出来呢,原因是我过了中测之后就觉得万事大吉了,根本就没有进行后续测试,到后来强测的败北自食其果。
homework3
这次作业我吸收了上一次的经验教训,进行了长时间的前期构思才下手去做。(也正是前期构思时间太长了,后期没剩时间优化55555)
前期构思
这次作业难度颇高,最重要的难点就在于表达式的迭代。这次是真的写不出来一个完整的正则表达式来判断WF了。为此我专门写了一个check函数,每次遇到括号就对括号内容进行一次递归检查。
解决了WF问题后,就是读取字符串。这一次作业考虑到式子的复杂性,我重构了读取字符串部分。在前两次作业中我都选择了按照项的正则表达式去split整个字符串,但这次显然不适用。于是我采用了逐个字符去读取,同时采用树的数据结构去保存表达式。读取的时候按照大一数据结构之中学过的中缀表达式转后缀表达式的方法。利用栈的结构辅助完成。期间关于三角函数,我将其视为一种单目运算符,它只有一个运算数(规定放在左子树上,右子树数规定为null),并规定其运算优先级与括号相同。(遇到直接压栈,弹出使视为运算级最低。)
构建好完整的表达式树后,求导就变得很简单。采用后序遍历,依次访问每个节点,每个叶节点只能是x或常数。给对叶节点求完导之后,开始访问其他节点。由于其他节点都是运算符,不同的运算符有不同的求导法则。每次对节点求完导后,就删除其两个子树。同时要记得节点要保存原函数(乘法原理时要使用)。这样遍历完所有节点后,根节点就会保存最后的求导结果。
UML图如下
度量分析如下
本次作业整体下来感觉工程量很大,绝大多数部分都重构了。但经过前期的构思,感觉整体思路比较清晰。但事无巨细,身必躬亲,真正做起来还是遇到了很多麻烦,最令我头疼的是最后求导时的加括号问题。
最后求导的过程实现起来感觉很简单,但难点在于什么时候加括号。这个问题困扰我了很久。我一开始选择莽,每一次求完导给它套一层括号就完事。但中测时就出现了bug。(是一个输出形式的bug。)仔细分析发现是乱加括号会导致
(formula)** number
这样形式的出现,就会导致WF。改正这一问题之后就通过了测试。
bug分析
这次强测出现了一个问题,互测中也被找到了若干错误,但是他们都是同质的。
这个bug是在读取字符串的过程中,遇到形如-(-x)这样的字符串时,会漏掉括号内部的负号。原因是在检查完合法性之后,使用replaceAll替代了所有连续的正负号,但却忽略了隔着括号内外存在连续符号的情况。修改后将多余的符号改成因子乘积形式,(例如-(-x)变成-(-1*x)),再进行后续处理。
作业反思
这次拿到题目的时候,心情是绝望的。在经历上一次的滑铁卢之后,我对作业产生了一些抵触心理(我只能爬)。我前期做了大量思考,阅读了很多讨论区大佬们的发言,也针对一些细节画了实现图。这样一来完成过程明显比上一次轻松一些。整体上来说我对这次作业还是很满意的,但对比它人代码之后依然能发现自己的许多问题。
- 优化为零,这次作业为了保证正确性,我基本没有做任何优化。一个20字符的式子求导后在我这里长度可能会翻十倍,重要原因是我无脑加括号造成的。
- 求导过程写了一个很长的方法,并没有使用老师建议的继承的方法。
总结反思
第一个单元很快就结束了,经历过大失败,但依然在摸索与前进。反思着四周的学习,我觉得自己最大的问题就是喜欢自己蛮干,而忽略了老师,助教所教授的方法。例如这一单元重点学习的工厂模式,在我的代码中基本就没有体现。在互测中阅读他人的代码过程中,才算学习了这种模式的具体应用的妙处。另一方面就是自己代码的扩展性,每一周自己好像都在重新做作业,这无疑是愚蠢的。
接下来的时间内,我觉得最能提升自己的方法就是阅读大佬们的代码,尽快将自己从面向过程的思路上转变为面向对象。
OO的确是一门恐怖的课程,让我感到了前所未有的挑战。但伴随着挑战的是不断充实自己的愉悦。话不多说,继续加油吧!