2020-面向对象-第一单元(表达式求导)

2020-面向对象-第一单元(表达式求导)

一、程序结构分析

homework1

  1. 整体架构思路

  1. 此次任务为简单多项式的求导,每一项的形式可以视为a*x**b(其中a,b为带符号整数),所以建立Poly{coef,exp}类储存每一项,并在Poly类中实现项的求导方法。考虑化简因素,覆写equalstoString方法。

  2. 在主体部分完成后,处理输入。

  • 将空白符去除

  • 对符号进行处理,如--替换为++

  • 构造项的正则表达式对整个表达式进行匹配,每找到一项便查重塞入Arraylist list

  1. 每一项调用求导方法后得到导函数的ansList。输出处理,注意找到正项先输出。

  1. 自我点评

  • 可取之处:先解决核心问题,完成主体(输入,求导)框架搭建,再处理化简等相关细节问题。Poly类的实现还算符合面向对象思想。

  • 改进之处:整体被面向过程地分为三大块,InputProcess,DealProcess,OutputProcess,且全部堆在主类实现,导致难以扩展。(第二次作业将InputProcess作为一个类封装实现)。

  1. 度量工具分析

    UML类图分析:
    2020-面向对象-第一单元(表达式求导)_第1张图片
    Statistic行数分析: 从行数可以看出Task1主类,功能性代码堆积的问题。

    Metrics复杂度分析: 其中Poly的toString方法存在较多判断分支语句,导致圈复杂度较大。而飘红的OutpuProcess则是面向过程处理输出的后果。
    2020-面向对象-第一单元(表达式求导)_第2张图片

homework2

  1. 整体架构思路

  1. 此次任务新增简单正余弦函数的求导,每一项可以视为a*x**b*sin(x)**c*cos(x)**d,所以在第一次Poly类的基础上新增sin,cos的指数,并改写求导方法,equal,toString方法,考虑简单化简{sin(x)**2+cos(x)**2,1-cos(x)**2,1-sin(x)**2}*f(x)新增similar方法。仍使用list查重储存每项元素。

  2. 将输入处理单独包装为一个类实现

  • 通过构造大正则表达式的方式匹配整个式子是否符合format.(大正则表达式由诸多小正则按层次相加构成,保证可阅读性,可复用性)

  • 利用item项的正则表达式将表达式拆分提取出每一项;每一项再送给DetailCut方法,利用factor因子的正则表达式析取,得到一个Poly实例加入list.(至此,主体架构搭建完成)

  1. 每一项求导后得到ansList,再对其进行简化处理,输出等细节处理.我做的简化处理比较简单,利用两个循环不断在ansList里寻找similar方法对两项进行判定化简,直到没有可化简项停止,最后输出.

  1. 自我点评

  • 可取之处:与work1一样先完成整体搭建,再考虑细节处理,比较有条理.且这次改进了输入相关的处理,根据层次封装处理,为下一次作业提供了部分可复用代码.

  • 改进之处:大正则的匹配方式只适用于本次的少量输入,这种方法不具备可复用性.化简相对较弱,导致此次性能分较低.

  1. 度量工具分析    
    UML类图分析: InputProcess
    类中均为static变量,是正则表达式的相关构成.
    2020-面向对象-第一单元(表达式求导)_第3张图片


    Statistic行数分析: 通过行数可以看出相比第一次作业,主类有了一些改进,主要功能代码的实现被类封装起来.


    Metrics复杂度分析:InputProcess因为大正则的处理复杂度较高,Task2主类则因为输出和化简的相关处理代码的堆积导致复杂度和耦合度都较高.
    2020-面向对象-第一单元(表达式求导)_第4张图片

homework3

在第三次作业背景下难以找到统一的方式存储各项元素,最终选择以字符串的形式对嵌套,表达式因子进行处理,这也导致整个项目的重构,但也有部分功能性代码在这次作业中复用.

  1. 整体架构思路

  1. 此次作业新增三角函数嵌套因子,表达式因子的处理,思考取舍后最终选择以a*x**b*sin(x)**c*cos(x)**d*NestFactor+*(SmallExp)+的形式定义每一项,+表示{0,};其中NestFactor,SmallExp以字符串存储,其它与第二次类似只记录指数项,但独立成类处理.所以一项Item持有Coef,Power,Sin,Cos,Arraylist,Arraylist;表达式Exp持有Arraylist.每一因子实现求导方法,在Item中实现乘法法则的求导(即各因子求导后与其余因子进行拼接得到新的Item).

  2. 所有Item构成Exp表达式,而Exp的求导即为对每一Item求导,所有导函数相加的字符串构成结果Exp.从而可递归调用Exp的求导方法,在NestFactor与SmallExp的求导方法中,核心均为调用Exp的求导方法.

  3. 输入处理

  • 合法性检查,这里仍然使用递归检查的思想,复用第二次作业的相关正则表达式(其中因子项增加#).首先识别(,判断该(属于三角函数还是表达式,如为三角函数则将括号里替换为x,括号里式子加入list0(因子list);如为表达式则将包括括号在内的(SmallExp)替换为#,括号里式子加入list1(表达式list).此时可对整个表达式进行合法性检查,之后对list0,list1元素分别递归检查,list0检查方式为因子匹配,list1则为表达式匹配.

  • 将表达式拆分成项,得到Arraylist list,Item利用每一项字符串生成实例,即复用作业2的DetailCut.(到这里,架构基本搭建完成,现在需要思考的是,如何让它能够递归求导)

  1. 递归求导,Exp类同样实现求导方法,流程如下.该方法的主要思想就是不断把嵌套因子,表达式因子作为字符串丢给Exp类的求导方法,把不属于基本类型的式子不断拆分,最终完成求导.

String Dao(String str){
Arraylist list = Detailcut(str);
Arraylist list1 = ...for new Item(string of list)
StringBuilder sb = ... for append item.Dao(Item of list1)//注意添加符号连接
return sb.toString();
}
  1. 化简.主要是在字符串拼接上做处理,把握因子toString,Dao方法的书写.大部分复用第二次作业toString的方法. 还可以在建立 ItemList的过程中不断传入嵌套因子和表达式因子的字符串建立新项从而得到最简的字符串形式.但因为还要进行递归处理,我在这次作业并没有使用.

  1. 自我点评

  • 可取之处:复用第二次作业的正则层次匹配,在这次作业利用递归对表达式进行拆分检查合法性;对每个因子以及顶级的表达式,均实现统一接口的求导方法,从而可以通过传入字符串即可递归求导,能较好地完成此次任务,并有一定的复用性.

  • 改进之处:在正则匹配中因为一处空白字符的漏写导致弱测bug花费较久时间才找出来,即匹配逻辑整体清晰,但需要注意很多细节;递归求导的思路也是框架容易搭建,但细节上的处理需要细细琢磨,个人认为这不是一种好的开发方法,应该有一种工程化方法来规避一些琐碎的细节问题,能更好地开发程序.因为采取的是递归求导的方法,所以耦合性较高,如最底层次(嵌套因子,表达式因子)又调用了顶级层次(表达式)的求导方法.

  1. 度量工具分析
    UML类图分析:按层次实现各类.
    2020-面向对象-第一单元(表达式求导)_第5张图片
     Statistic行数分析:从行数可以看出功能性代码被不断分向各级调用现场,主类被精简为输入,调用各级方法.
    2020-面向对象-第一单元(表达式求导)_第6张图片
     Metrics复杂度分析:截取部分飘红的方法,可以看出被递归调用的方法复杂度都比较高  
    2020-面向对象-第一单元(表达式求导)_第7张图片
    2020-面向对象-第一单元(表达式求导)_第8张图片

注:以上三次作业整体架构思路略去了较多实现中需要注意的细节问题,仅是我在构思作业时的大体框架.

二 、Bug and Test

Bug

自己的Bug情况

  • 在三次强测与互测中均未被测出bug;

  • 在第一第二次作业中均一次通过中测,在第三次作业中被中测的两个弱测点卡了较久.

  • 对第三次week4,5的分析:对这两个测试用例,我的程序都是因为在判断合法性方法中因子正则表达式的构造漏打了可能出现的sp([ \\t]),导致出现正确形式被误判为WF的情况.这也就是我在自我点评中所说的缺点,如果不能在本地复现这类错误,除非能仔细检查思考代码,否则难以找到bug.而我在本地做了一些测试后认为求导部分没有问题,便开始怀疑是WF误判的问题,在同学的经验启示下,通过构造一些样例并检查相关代码,最终发现了自己的疏漏.

互测的Bug情况

  • 三次互测基本都是每次能hack到3个非同质bug,三次都主要是输出优化时toString方法中一些细节没有考虑清晰,第二次和第三次存在使用容器时如HashMap,ArrayList对一些空情况考虑不周而运行错误的情况.

Test

  • 第一次作业没有搭建评测机,纯手搓数据,构造的都是些边缘样例,虽然较弱,但也意外地在互测中hack到了别人的bug.对自己的评测也是手搓数据,以及开发中对每一步的功能验证.

  • 第二次作业搭建了一个半自动评测机,通过大佬们在讨论区传授经验的帖子,用xeger批量生成测试数据,再用sympy求导计算并对比待测输出与py输出.因为不太会使用命令行sh对拍,所以是通过获取输入输出文件每行内容的方式去对拍.存在一定的手动工作量.但这样大量的随机数据还是有效的,能够找到互测屋中的较多bug.

  • 第三次作业即在第二次基础上更改了正则生成的相关表达式.

  • 随机数据比较难结合被测程序构造,结合特定程序的代码结构设计测试用例一般是在看到这份代码对优化处理比较多的情况下,特意构造一些边界数据,比如0,1,x**2以及一些括号不能省略,或是括号里的内容不能省略的数据.

三、应用对象创建模式

采用了简单工厂模式

  • 第一次作业由于仅有一个类,就直接匹配到相关数据就生成项.

  • 第二次作业以及第三次作业都是通过传入字符串作为参数,进行项的构造,这样不仅能很好地复用,而且创建实例对象的逻辑不会暴露在外导致被更改.

四、心得体会

  • 难度不断增加的三次作业,让我的水平从边写代码边查询java语法,到熟练使用相关容器,能正确利用正则表达式析取输入.虽然OO课程的体验比较累,但能感觉到OO的训练是卓有成效的.通过三次迭代作业以及视频学习,我初步了解了OOP的一些基本思想方法,开发模式,也在实践中感受到可扩展性,高内聚低耦合开发的重要性.

  • 在手搓数据测试困难的背景下,也通过学习讨论区大佬的经验帖搭建了一个弱弱的半自动步枪,还是非常有收获的.

  • 听说下周就是电梯在等着我们了,希望能继续努力,不断进步.(先好好休息一周,重整行囊再出发)

你可能感兴趣的:(2020-面向对象-第一单元(表达式求导))