301
社区成员
发帖
与我相关
我的任务
分享目录

第一次作业我认为是第一单元最困难的部分,因为整个架构是从0开始的,下面是我搭建hw1架构的过程。
首先我们要掌握并学会应用递归下降的思想,它是一种很有“面向对象”风格的表达式解析方式。它的原理可以表述为:
表达式 = 项 +/- 项 +/- 项
项 = 因子 * 因子 * 因子
这样一来,千差万别的表达式被抽象出了一种规范,便于我们形式化地对其进行处理。
接着我们把注意力集中在递归下降的基石——因子上。表达式的特点是明显的,它是项用加减号连接;项的特点是明显的,它是因子用乘号连接;但因子的特点显得不太好概括,于是我选择将因子作为抽象的父类,根据题目要求将其细分成表达式因子、数字因子、幂函数因子三个子类。
根据上面的思路,我们已经能够着手写代码了。创建Expr类,Term类,ExprFactor类,NumFactor类,PowerFactor类。将读入的字符串在Processor类里进行去空白符等预处理,再送往Lexer进行词法解析,把词法解析出的内容送到Parser进行语法解析。至此,一个表达式就能被顺利分解成如下形式:

完成上面工作,说明我们已经掌握并应用了递归下降方法。但它仅仅是解析表达式的一种方法,是一种工具。我们还要结合题目的需求,并利用好这把“工具”,才能顺利完成第一单元的作业。
再看一眼题目要求,是去括号化简,即“输入表达式 = 右式”,这个右式要没有括号且等于左式。所以递归下降只是将左式进行了规整的划分,现在问题是我们该如何看待右式。我从往届的博客中得到了启发,就是将它看成一个多项式,多项式的构成更加简单,具体如下:

所以,我们的下一步是在所有类中实现toPoly方法,poly的属性是一个单项式容器,单项式的属性是x的系数和指数。当我们在所有类中实现toPoly方法后,对Parser语法解析后的表达式进行toPoly,这个方法就会一层层进行,最后返回一个没有括号的Poly。我们再把Poly转成字符串输出,第一次作业就完成了。
下面展示第一次作业的处理流程:

第二次作业增加了自定义函数和指数exp,看起来比较复杂。但只要对第一次作业理解透彻并且明确增改目标,我认为实现起来并不太困难。
我先完成的是对exp的新增。它的到来让我们不仅需要新增一个类ExpFactor,而且还要增加单项式的属性:单项式除了指数和系数外,还有了exp上的内容。
在自定义函数部分,除了需新增自定义函数因子外,还需增添对输入的处理,由题目我们会在读入待化简表达式前读入自定义函数的定义。而调用函数则是一个用实参替换形参的过程。具体替换过程如下:

第三次作业增加了可调用已定义的自定义函数和求导的功能。
为实现可调用已定义的自定义函数的功能,我仿造处理表达式的方法为函数另写了一套Lexer和Parser,最后很好的完成了这项任务。
对于求导功能,我们可以新增一个DerivativeFactor,再为所有表达式,项和因子实现toDerivative方法。实现求导功能最重要的就是要利用好课程组提供的这些求导公式:



三次作业的UML类图均粘贴于此,可以据此看出整个迭代过程


三次作业,行数从508->926->1202

可以看出有关解析的类的所有非抽象方法的平均复杂度都比较高
一般都是需要深拷贝却只是随意地用等号进行了浅拷贝导致“莫名其妙”的对原有对象的修改。我的解决办法就是把需要的对象重新new一个出来使用,以下是我对深浅拷贝的直观理解:

每次完成作业后,即便架构思路都很清晰了,但还是会在debug上额外花费很多时间,印象最深的是第二次作业里exp中替换x时的问题。
这是细节逻辑上的问题,跟java语法等的关系不大。这类问题可以说很难避免,只能在第一遍写的时候就考虑仔细,不然只好在面向数据debug时一点点调试了。
我实际上只做了一个优化,就是合并同类项。我认为它最有必要并且实现上也并不困难,毕竟我们有单项式这个专门的类,只要在每次相加减或相乘的时候做好合并就好。对于其它的优化,由于性能分损失并不多不过主要是不想多考虑了,就没有实现。
第一单元结束了,我通过了三次作业的所有中测强测互测,在第二次作业中利用别人的评测机hack成功一次,全程比预想要顺利一些。
但是在评测机搭建上面还是没有任何进展,并且debug的手段过于单一,我认为这些能力是肯定有必要再提升的,不然会重现oopre_hw3的痛苦。