BUAA-OO-2024-Unit1

 第一单元的主题是使用递归下降法处理复杂表达式,旨在通过此任务掌握从整体把握各层次的架构设计方法。

一、架构设计

1.基本构思

在第一次实验提供的结构基础上,我沿用了使用递归下降法进行表达式分析的思路。同时注意到题目中无论是因子、项或是表达式,都可以统一到一种多项式的形式,即多个单项式之和,并将这一思想在三次作业中一以贯之,使得我在面对迭代的需求增加时,只需调整单项式的具体内容,而不用在架构上进行重构。

在hw1中我建立了Mono(单项式)和Poly(多项式)两个类来统一表达式、项和因子这三个元素。使用接口Factor(因子)来管理常数(Number)、幂函数(Variable)和表达式(Expr)三种因子,这里表达式作为一种特殊的因子也使其实现Factor这个接口。在最终成型的架构中我并未使项(Term)也实现Factor接口,现在反思来看,无论是从代码的规整性,还是他们具体的方法之间的关系,应该也让Term实现Factor。

2.逐步迭代

在hw2中增加了自定义函数、指数函数和嵌套括号的需求。嵌套括号可由递归下降法自然地解决,而指数函数也可以调整单项式的具体形式为coe_{i}*x^{pow_{i}}*exp(poly_{i})来实现。对于自定义函数,有两种处理思路,一种是在表达式作为字符串输入时便把形参替换为实参,另一种是在进行词法语法分析时将实参代入,这里我采用了前者。考虑到无论是输入还是输出,都是以字符串形式,那么直接在字符串层面上做形式上的处理将带来便利。

在hw3中增加了求导因子以及自定义函数因子支持调用其他已定义函数这两个需求,后者在hw2的架构下可以无需改变便可解决。在将所有表达式统一到若干单项式之和后,求导因子的需求可以通过对单项式求导做形式上的求导,即为coe_{i}*pow_{i}*x^{pow_{i}-1}*exp(ploy_{i})+coe_{i}*x^{pow_{i}}*exp(ploy_{i})*dx(ploy_{i}),其中对poly的求导dx(poly_{i})又将使用递归下降法。

3.最终架构

经过开始的构思和二次迭代后,最终形成的数据类图如下。

除了数据类,还建立了功能类,在面向对象的程序设计中,一切都是对象,尽管这种功能类更接近于面向过程中函数的概念,但仍然将其建立为一个类,具体的UML图如下。

4.继续迭代

一种可能得迭代场景是增加其他类型的函数因子,如三角函数sin()和cos()。当面对这种情况时,保持总体架构不变的前提下,只需在单项式中加入三角函数因子即可。由此可见将表达式做统一的形式处理具有强大包容性。

 二、程序量化分析

1.代码行数统计

从代码行数可以直观看出,整个程序的主体集中在Lexer、Mono、Parser和Poly这些功能类中。

2.复杂度分析

使用IDEA的MetricsReload工具,分析程序的圈复杂度。为了更好地理解分析结果,解释几个名词的含义。CogC(Cyclomatic Complexity):圈复杂度,表示程序中的独立路径数目。ev(Essential Complexity):本质复杂度,表示程序中必须要有的控制流程数目,即如果减少程序中任何一条路径,则程序将不完整。iv(Inherent Complexity):内在复杂度,程序本质上的复杂度。表示程序在没有框架、库、编译器等支持时的复杂度。v(Volume):程序体积,表示程序中的独立语句数目。其中,最为常用的是圈复杂度(CogC),圈复杂度越高,说明程序的复杂度越高,可能会造成程序难以维护,测试、调试等其他问题。我的程序的方法复杂度如下(仅展示复杂度相对较高的方法)。

分析得到复杂度较高的方法仍大量聚集在功能类中,尤其是Paser中的parseFactor方法,这是由于许多数据类都实现了Factor接口,那么在对这些数据类进行语法分析时便会调用这一方法,导致其圈复杂度过高。

这一点也可以从下面类复杂度的展示看出,为了便于理解,仍然介绍几个术语。OCavg:average Operation Complexity,计算每个类中所有非抽象方法的平均圈复杂度,继承的方法不计算在内。OCmax:maximum Operation Complexity,计算每个类中非抽象方法的最大圈复杂度,继承的方法不计算在内。WMC:Weighted Method Complexity,计算每个类中方法的圈复杂度总和。

三、bug分析与优化

1.bug分析

在hw2中因未充分考虑指数过大的情况,导致(((((((((((x^8)^8)^8)^8)^8)^8)^8)^8)^8)^8)^8)^8这一样例的指数超过int范围,在将所有int类型的指数改为BigInteger后可解决这一问题。但需注意的是,在改变属性的类型时,同时也要将所有用到这个属性的方法的参数类型做覆盖性修改。

另一个bug仍然出现在hw2,由于当时没做任何优化,只是完成了功能需求,未做性能上的改进,导致某个测试点超出内存,同时这也启发我在hw3中的优化势在必行。

2.输出化简

在hw3中,首先我做了合并同类项的化简,即在幂函数的幂次相同、指数函数的指数部分相同的情况下,将系数相加合并。在此基础上,我判断了指数函数exp()括号内是否为0,当其为0时,可以直接不输出exp()部分,这将大大减小输出表达式的长度。

四、感悟与期待

通过第一单元的学习,在作业中的实践中,我渐渐理解了理论课上介绍的程序设计中结构层次设计的重要性。并从递归下降这一角度为切入点,体会到面向对象的程序设计与例如C语言式的面向过程的程序设计的不同。在C语言中,递归好像是一层一层直到遇到结束条件,但在面向对象中,只需考虑当前类的当前对象的行为,封装性体现得很明显。

对于课程建设,希望互测环节能根据房间的不同级别调整base分的占比,例如对于A类房间,由于同学们的程序鲁棒性强,不容易发现bug,可适当提高base占比。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值