BUAA面向对象2022第一单元总结
总览
作业内容
本单元的主要作业内容为表达式拆括号,共有3次作业,为迭代开发。
第一次作业只有加减乘次方以及单层括号。
第二次作业加入了嵌套括号,简单三角函数,自定义函数和求和函数。
第三次作业加入了嵌套三角函数和嵌套自定义函数。
完成情况
我在三次作业中使用了递归下降法,坚持迭代开发,没有进行过重构。
三次均有bug,但是都满分通过了强测。(这强测是真的弱)
性能分:在第一次作业中获得满分,第二三次作业中由于没有进行足够的三角优化,扣了不少
互测情况:三次作业 发起/受到hack 分别为 2/12 , 2/3 , 5/3
bug分析
第一次作业:在输出第一个正项时,如果是常数项,会多输出一个*
第二次作业:遇到sin(-1)**2时,直接把符号提了出来,即-sin(1)**2
第三次作业:遇到sin(sin(-1))时,会把里层括号的负号算2次,即sin(sin(1))
纯nt
实现过程
总体思路
先将输入的自定义函数和表达式经过一定的处理,后使用parser和lexer对表达式进行递归下降解析。
利用字符串整体替换并加一层括号实现函数的调用。
使用两层HashMap对结果进行存储,便于合并同类项。
UML类图
UML类图解释
除了接口的实现,我把联系紧密的类用直线连了起来。
MainClass:输入与输出
ChangeString:去掉空白符以及将多字符的符号(例如**)换成特定单字符符号(例如^),便于lexer和parser解析
Lexer:扫这个表达式
Parser:对表达式进行递归下降,当遇到不同的符号/数字时,执行不同的解析方法。
Expr:表达式,由若干项组成,+
连接,startFh为第一个符号。
Term:项,由若干因子组成,*
连接,包含了startFh为第一个符号。
Factor:因子,分为常数因子,变量因子,三角函数因子,自定义函数因子,求和函数因子,表达式因子。其中getAns()方法会返回化简后的表达式。
Number:常数因子,由一个常数组成。
Variable:变量因子,由x
和指数组成。
SanJiao:三角函数因子,由三角函数类型和内部表达式以及指数组成。
FindVariable:找出自定义函数和求和函数中由,
分隔的各个'项'。
ZiDingYi:自定义函数因子,经过替换处理后看作表达式。
Sum:求和函数因子,经过替换处理后看作表达式。
SimpleFactor:化简后的因子。
Polynomial:化简后的多项式。
Node:化简后的三角函数内部的玩意。
这里对Polynomial和Node进行一些额外的说明。
Polynomial中的第一层HashMap的key值为化简后的各个项,value为这些项的乘积的系数。
即:(Value:coeff)(Key:*sin(X)^a*...*cos(Y)^b)
第二层HaspMap的key值为某个sin(X)^exp中的X,value为其中的exp
在第二次作业中X一定可以用a*x^b表示,因此我用两个变量将a,b存储。
可以发现这样存储之后对于Polynomial的运算会很方便。
在第三次作业中,由于X可以是一个Polynomial,但是我发现只需要将Node中的a,b变成一个Numbe/Variable/Polynomial(也就是SimpleFactor)就可以沿用之前的所有运算操作,不需要任何其他的改变。
结果化简与输出
对于sin(0)直接返回0,cos(0)返回1。
对于sin内部第一项系数为-,将其内部取反外部加负号。
cos同理,只是外部不加负号。这样可以消掉一些恰好为相反数的项。
输出时采用递归输出,对于三角函数,可以判断其内部是否是一个常数或是一个系数为1的因子,如果是则只用套一层括号。
复杂度分析
代码总行数:841
优点:每个类想实现的功能比较清晰,类之间的耦合度较低。
缺点:有些类在实现起来的时候实在是过于丑陋,以至于写出了bug
可以看到,在方法复杂度中,采用了较多if的方法复杂度较高,事实上我的错误也都错在这些方法。
第一次是Polynomial.toString(),二三次是SanJiao.getAns()。
因此提高代码的结构化是降低bug出现概率的重要手段。
总结与反思
代码架构
这次我对自己的代码整体架构还比较满意。在第二次到第三次的迭代中,我几乎没有进行太多修改就完成了任务。不过这主要还是因为递归下降的板子写的好,如果让我自己写,我很可能写不出来。
当然我也有一些不足,比如搞了ChangeString和FindVariable这两个奇怪的类,其实我只是为了写一个静态方法。还有就是Polynomial的运算实际上只用传一个参数,我传了两个,就看起来很蠢。
关于互测
其实我并没有认真读别人的代码,只是用了一些看起来容易错的数据,然后就hack到了一些人。
反思
这三次的bug实在是不应该。虽然都是在优化的时候写出来的bug,但是bug都非常明显。这说明我写代码和测试时都不够认真。
一些建议
可以把性能分最高的结果展示出来。
可以加一些额外任务,例如不合法表达式的判断,sum函数的嵌套等(作为额外的测试点),这样卷王们就有更多事情可以去做了。