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

表达式解析:递归下降
对于原始输入,在Lexer类中使用正则表达式消除其中多余的空白项、正负号以及数字0,再将表达式中所有的语法单元解析并存储在数组中
在Parser类中,将表达式层级划分为:表达式Expr→项Term→因子Factor
因子Factor包括三个子类:表达式因子ExprFactor/幂因子PowFactor/数字因子NumFactor
表达式展开:单项式与多项式
观察输出可知表达式最终可以化简为一个多项式形式:Expr = Σa*x^n,其中a是系数,n是幂,故新建Poly(多项式类)和Mono(单项式类),并重写 Mono的toString()方法用于输出
另外,在Factor中设置toPoly()方法用于将其转化成多项式,并利用Poly中的方法进行化简和合并
本次作业没有遇到bug
复杂度如下图:

对于最终得到的多项式Poly,对与其包含的所有Mono,将其中指数相同的项分别合并,缩短最终输出的长度

在第一次作业基础上增加了指数因子和自定义函数因子,递归下降解析仍然适用
Factor子类更新:
表达式因子ExprFactor/幂因子PowFactor/数字因子NumFactor/指数因子ExpFactor/函数因子FuncFactor
Token类需要增加相应的语法单元:FUNC,EXP,COMMA
对于自定义函数的处理,新建了一个工具类Definer
方法addFunc()负责解析函数定义式,并将函数定义式和形参列表分别加入相应的HashMap
方法callFunc()在函数调用时使用,传入的参数是函数名和实参列表。这个方法首先根据函数名获得形参列表,然后由形参和实参的对应关系建立一个映射,最后 遍历函数定义式,将定义式中的形参替换成实参
单项式形式变化
单项式新增了指数函数的部分:ax^nexp(factor)^m
修改Mono类的toString()方法以适应新的输出形式
忽视了自定义函数的定义式中可能含有的空白项,在Definer类中没有进行相应的处理,导致无法解析含有空白项的函数定义式
幂因子PowFactor的幂属性设置为int类型,导致最终结果化简过后可能超出int范围
复杂度如下图

本次作业的性能优化主要针对指数函数进行
首先将指数函数的幂乘到括号内因子的左边并对新的内部因子进行化简,最终所有指数函数的幂次都变成1
对于Mono中多个指数函数相乘的情况,由于其幂次都是1,所以将其括号内的因子相加,最终只剩下一个大的指数函数,省下了多余的“exp()”所占用的长度
对于指数函数括号层数的问题,在Mono的toString()方法中加入特判:若由内部因子的toString()方法得到的字符串长度大于1,则额外加一层括号,否则不加括号

在第二次作业基础上增加了求导因子
新增工具类Deriver,用于处理求导因子
在Mono类中新增getDx()和getDoubleDx()方法,用于对表达式进行求导
在Parser类的parseFactor()方法中,对于求导因子,直接调用Deriver的derived()方法,得到求导后的表达式,并返回一个表达式因子
由于过度依赖递归下降解析以及toString()方法,导致在处理某些复杂表达式时出现运行时间过长的问题
在进行化简并合并同类项时,由于遍历及克隆的操作,可能会抛出ConcurrentModificationException
复杂度如下图

为合并同类项的操作设置使用限制,在Poly的MonoList过长时不再化简而是直接进行输出,避免超时
参考第二次上机实验的代码,为所有Factor类、Term类以及Expr类设置clone()方法,在化简方法中使用对象的clone()方法而不是直接对对象本身进行操作,避免ConcurrentModificationException报错
这个单元的学习让我对于递归下降的思想有了深入的理解,使我面向对象编程的思维有了非常大的提升。
由于第一次作业就采用了递归下降解析以及较为合理的架构,所以在后续的作业中没有进行大规模的重构。在面对新的需求时,只需要在已有的框架上进行增量开发即可,这是我对于自己在本单元学习过程中比较满意的地方。
但是,由于过于依赖递归下降解析,到第三次作业时,我在Parser、Deriver等类中都调用了Parser方法,导致我的代码在面对复杂输入时递归层数过多,最终不能在时间限制内得到输出,这是我在架构前期没有考虑到的,而到了这个阶段已经很难跳出现有框架去解决问题了。
debug方面,非常感谢cxc同学写的测评机,为我省去了自己搭建评测机的时间,找出了很多bug。
性能分方面,个人认为从第二次作业引入指数函数开始,想要拿满性能分就变成了一件非常困难的事情。所以,我只针对一些比较普遍的情况进行了相应的优化,最终在强测的大部分测试点中都能拿到90以上的分数,因此我觉得这种程度的优化对我来说是一个非常性价比的选择,多余的时间可以放在其他有意义的事情上。
最后,特别感谢课程组及助教团队,每周的研讨课能够让我吸取其他同学的经验与思路,而实验代码则是为我的作业架构提供了宝贵的思路,这些内容都让我受益匪浅。