2023BUAA OO 第一单元总结

第一单元包含三次作业。

第一次单纯的解析表达式,输出计算结果,并且不支持括号嵌套。

第二次作业需要支持嵌套括号,并且可以调用自定义函数,支持三角函数。

第三次作业增加一次求导,且函数需要支持调用函数。

三次作业难度评价为2>1>3 ,1,3的排序也许是因为后面逐渐熟练,但也和自己的结构布局的实现有关,接下来便是三次作业的分析。

Homework1

类与方法的度量

类名属性个数方法个数总代码规模(行)
Append0251
Check0598
Expr2770
Variety2423
Lexer3437
Main0351
Number2425
Optimize04156
OptLexer3546
Parser1484
Term28108
Factor028

为了便于观察和排版的美观性,每个方法的规模、每个方法的控制分支数目将在类图中给出,三次作业均沿用此方式。

类的内聚和相互耦合情况分析

 

 可以看到,复杂度较高的类是Optimize,在该类中进行了最后的输出处理,将解析后的Expr输出为字符串。内聚性过强,特化了可处理的内容,降低了复用程度。并且对于后面的迭代意义不大,因此后面的作业我更换了架构。

类图

 优点:思路清晰,便于维护,可以精确定位到每一步的内容。同时能避免指数符号‘**’对于乘法‘*’的影响。我用9位数字存储xyz的指数,每三位表示一个变量var的指数,便于管理。

缺点:过于固化,各部分功能难以支持更多的扩展,后面需要重构。

Homework2

类与方法的度量

类名属性个数方法个数总代码规模(行)
Cos3442
Sin3442
Single515254
Parser1498
Expr3552
Variety3559
Lexer3785
Main0260
Num1532
MakeFunc05145
Promote0432
Poly15104
Term1222
Factor039

 与第一次作业相比,增加了两个类sin和cos,删除并更换了几个类。原因便是结构和思路都发生了变化,具体内容会在下文架构设计处解释。

类的内聚和相互耦合情况分析

这里只展示了复杂度较高的部分,集中在对sin和cos的处理上。sin(Factor)中可以嵌套表达式,因此在存储和判断上较为复杂,如果要判断两个sin相等,就需要遍历它们的子表达式,这样会极大的增大开销,因此我以字符串的形式存储sin和cos的内容,便于比较和遍历。

类图

 

 优点:使用两次递归下降,可扩展性较好,只需要使新因子实现Factor即可,同时将指数的信息保存在Factor中,在多项式处理时再展开,可以处理更大的指数,更多的连乘,降低了开销。

缺点:递归较多,debug查询比较麻烦。对于内存开销比较大。

Homework3

类与方法的度量

 

类名属性个数方法个数总代码规模(行)
Cos3463
Sin3462
Single515254
Parser1498
Expr35112
Variety3577
Lexer3785
Main0286
Num1550
MakeFunc05145
Promote0432
Poly15104
Term12110
Factor0315
DeParser04137
Derivation0363

类的内聚和相互耦合情况分析 

 在作业二的基础上实现了一个求导功能,该功能复杂度较高,但符合规律。能够支持常数,变量,表达式,三角函数,自定义函数的求导。

类图

 优点:作业二的架构基础上增添了求导模块,不会影响原功能,并且可以直接删去该模块而不影响原功能

缺点:在求导模块的优化不够,如果遇到极大规模的数据,可能运行时长会很长,在这里也错了一个点,原因是time limit exceed

架构设计体验

三次作业的架构理论上应层层递进,迭代开发,但是第二次作业的跨度挺大的,支持嵌套,支持自定义函数,支持三角函数。我第一次的架构通过类图可以看出,在递归下降解析完表达式后,是通过数字的不同位来输出字符串的,比如 001 020 003 输出 x*y**20*z**3,这个构造方式在作业二中具有极大劣势,难以表达sin和cos内部的因子,如果内部因子过大,就很可能出现各种错误。

因此我也通过这个机会进行彻底的重构,恰好上了一节研讨课,同学们给予了我启发,我使用多项式和单项式的方法来消除指数和表达式因子相乘的问题,在递归过程中将相同项合并,存储在最终的poly中,这个架构的优势在于他是逐层进行的,不需要最后的综合。可以直接输出sin内部的因子成为字符串,在sin中储存一个字符串即可。

第三次作业沿用了第二次的架构,通过类图可以看出,只是多了两个新类,并且对factor的实现类增加了几个方法。遍历一边输入,将求导因子提前运算,返回完成后的表达式。这样就回到了第二次作业的情况,直接调用方法即可。

总的来说,重构不一定是坏事,一个良好的架构能省去很多后续的工作,并且越早发现自己的不足越好。在结构上可以将功能模块化,使不同功能的耦合度降低,达到拆去模块不影响工作的效果是最好的。

程序bug分析

作业1:

强测没有错,互测错误在于数字位数,我错误的估计了评测机的代价要求,最开始用6位数即00 00 00 来存储xyz的指数,上限99,结果同学使用一个y**104的结果帮我查出了这个漏洞。

该出错方法行数和圈复杂度并不高,是我设计时的错误,改为000 000 000 就能解决问题。

作业2:

强测错在 形如-(x+y)**0 的表达式上,没有输出负号,互测测出一个 sin(0)**0  ,应该得1 却输出了0,经检查,问题均出在指数为0的 表达式的判断上,没有特判负号和表达式为0的情况,导致了错误输出。

该方法复杂度较高,由于对于指数0的判定写在了方法的最后,导致了遗漏。

作业3:

互测没错,强测一个点超时,原因是对于一个极大数据求导,会产生极多项,比如 dx( x*1*1*1),会输出4项,1*1*1*1,x*0*1*1,x*1*0*1,x*1*1*0,我没有处理乘积为0的项,而这些项在返回时又会被拆分为一个个表达式,导致极大的增加了不必要的计算开销,造成了超时,后来增加一步判定便解决了问题。

该方法复杂度较高,问题也在于设计时未考虑过大数据的开销问题,以后对于这种临界问题要多加注意。

发现他人bug

每次写作业时,我会顺手记录在思考过程中想到的特别样例,以及调试自己程序时发现的错误点,同时通过阅读测试对象的代码来思考可能的错误。

三次互测都有成功发现他人的错误,大部分错误是通过 特别样例 查找到的,原因可能是在构思阶段思考的比较多,会设想一些特别的样例来测试自己的程序,如果有同学思考的不全面就可能出错。其他错误便来自精准代码打击。作业很容易在格式判断方面,特殊情况上找到错误,一般查看最多的便是数据预处理和字符串的解析,结构上很少会错,要不然也过不了中测。

心得体会

实话实说,对于自己这一单元的成绩还是知足的,(虽然就差一点点就能更好ㄒoㄒ)。我在上学期没能抽上先导课,自己在寒假学习了Java语法。但是还是在第一周被打了个措手不及,只会语法而在结构设计上一窍不通,自己查找资料学习,向同学询问,苦熬三天完成了第一次作业。一出来强测全过,当时是十分高兴的,证明自己的努力没有白费。第二周的作业一出来,发现自己需要彻底重构,我吸取同学们的经验,加上自己的理解,又是大战三天取得最终战果,当然也是累得不行。不过第三周就好多了,有了清晰的思路,一个下午就完成了迭代。

这一个月的体验深刻地告诉我,人在认真的时候是真的能挑战自我,从无到有的完成一项艰巨任务是很有成就感的事情。唯一的遗憾就是那几个测不出来的错误点,不过人要知足常乐,分数平均下来还是很不错的,下一单元要更加努力。

感谢努力的自己,感谢同学,更要感谢助教和老师们的帮助。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值