第一个单元的三次作业均为求导,循序渐进的让我们掌握如何构造类和方法,让整个代码是面向对象的设计而不是面向过程的设计。如果第一次作业和第二次作业你只是简单的对过程着手架构类,到了第三次作业就会变得格外麻烦。掌握了面向对象创建多个类、分层次地实现每个类的功能,并梳理清楚继承与接口处理每个类的思路,便能够游刃有余地解决。

一. 总体设计思路

第一次作业

第一次作业时,还没有建立面向对象的程序设计思维和架构类的思路,因此整个代码采用面向过程,只构建了一个类,用多个方法划分,来处理输入并打印出求导的结果。

(1)构建方法wrong来处理输入,判断是否输出WF,如果不是则继续求导,而在wrong方法内用正则表达式按[+-]匹配拆分的方式匹配(不采用一个大正则来匹配WF来防止爆栈)

(2)将式中所有的空格和\t用空串取代,然后用正则表达式match类和group类提取出*分开的每一项,并调用方法xishu和zhishu将每一项的系数和指数存入Arraylist中,在求导前先调用Hebing方法进行合并同类项进行化简。

(3)将合并后的每一项的系数和指数通过方法derivate进行求导,输出每一项求导后的字符串,在方法中判断如果系数为0,则为空字符串,如果系数为1或-1,则约去“1*”,如果指数为0,则输出系数,如果指数为1则约去指数“^指数”。进行化简操作。

(4)打印:

1. 将系数大于0的项(如果有的话)换到最前面(表达式长度-1)

3. 如果不是第一项且此项为正,输出一个正号。

5. 如果最终结果为空字符串,则输出0。

第二次作业

(1)在Polymonomial类中,与第一次作业同样的用[+-]划分逐项匹配,判断合法性来输出WF。

(2)将表达式逐项拆解,用monomial类提取表达式中每一项的系数,x的指数,sin(x)的指数和cos(x)的指数。

(3)将monomial类提取出来的四个参数传入derivation类,先分别对x,sin(x),cos(x)求导,然后根据乘的求导法则,组合在一起,再前面加上“xishu*”,如果系数为0,则为空字符串,如果为1或-1,则省略掉“1*”。

(4)将求导后的字符串组合在一起,再用hebing类进行合并同类项。

(5)打印规则如第一次作业。

第三次作业

(1)按照表达式(expression)、单项式(monomial)、因子(factor)的层次,分别建立三个类,进行递归的求导和合法性判断。

(2)将输入通过expression类进行[+-]的划分,划分的每一项传入monomial类,在monomial类里进行*的划分,每一项传入factor类,在factor类进行因子判断,如果为普通因子如x,sin(x)和cos(x),则根据求导法则进行求导,将结果字符串return回去;如果存在嵌套括号,则判断其为嵌套因子,通过正则匹配group提取出嵌套的因子,递归用factor处理,并return其求导结果;如果是表达式因子,则传入expression类,进行递归return其求导结果。

(3)逐层递归地返回结果为字符串类型,最终在main里完成输出。

(4)打印规则如第一次作业。

二. 自我测试发现的bug

第一次作业

(1)爆栈错误

开始时判断WF,采用的是通过一整个大字符串来进行匹配是否配对,当输入500个“+x”后出现了爆栈的错误,故修改wrong方法,通过[+-]进行划分逐项匹配,用一个小字符串正则匹配每一项,最终成功解决了爆栈问题。

(2)划分错误

划分采用的是通过[+-]分开,但[+-]不仅会出现在项与项之间,还会出现在指数为有符号整数里,故这里采用replaceAll将所有的“^+”和“^-”替换为“^zheng”和“^fu”,分开以后,再将所有的“zheng”和“fu”替换为相应的正负号,以免出现错误。

(3)优化改进

在优化过程中,通过与同学讨论,除合并同类项外,还可以在最终结果中寻找系数为正数的项,如果寻找到,则将其提前到首项再省去第一个字符“+”。故遍历最终结果的每一项, 将找到的第一个正项与首项互换即可。

第二次作业

(1)无输入情况错误

中测通过一个错误数据点发现无输入报错的问题,修改input方法,用hasNextLine()判断是否有输入,如有则进行操作,无则输出WF,解决了这个问题。

(2)正负号个数问题的处理

过了中测以后,通过自己设计测试数据,发现了对于正负号的判断,后面接整数和后面接x/sin(x)/cos(x)情况有区别,在PolyDerivationpolynomomial类的wrong方法里增加特殊情况判断,解决此情况。

(3)划分错误

相比于第一次作业,这里会出现[+-]的情况还有每一项连乘之间的有符号整数,因此增加replaceAll另外将所有的“*+”和“*-”替换为“*zheng”和“*fu”,分开以后,再将所有的“zheng”和“fu”替换为相应的正负号,以免出现错误。

第三次作业

在构建类和递归方法过程中没有很大问题,很快便能够正常的处理嵌套因子和表达式因子的各种求导结果,但仍旧出现了一些问题。

(1)输入中出现不必要的括号

在写好求导类的处理并准备判断合法性时,发现当输入“((sin(x)*x))”时,进入monomial类处理时,用乘号*进行划分每一项分别是“((sin(x)”和“x))”,无法正常进入factor类求导,最终会输出WF。

因此,我在expression、monomial和factor类里新建了一个去掉多余括号方法的类,根据入栈出栈的思路,让i由0开始移动,直到移动到可以完全去掉的左括号那里,对称的去掉输入字符串的两边,再进行处理。

(2)优化错误

不优化一时爽,一直不优化一直爽,本次作业因为嵌套过于复杂,为了避免错误,每一项输出时都最好在外面增加“()”,在处理优化时,根据前面入栈出栈的思路,仅能处理整个结果最外面的多余括号,而无法继续改进。

在通过“+”连接时,如果后面为空字符串,则不加“+”

本来试图对结果字符串进行化简处理,如果遇到“^1”、“*1”和“*0”则进行化简处理,但却在遇到“*100”和“*002”时遇到错误,受时间关系没有办法成功处理,最后只能遗憾地放弃。

三、代码分析

根据参考往届学长们的博客,可知三个总结程序结构的度量参数具体含义如下:

(1)ev(G):基本复杂度,用来衡量程序非结构化程度的,范围在[1,v(G)]之间,值越大则程序的结构越“病态”。非结构成分降低了程序的质量,增加了代码的维护难度。

(2)Iv(G):模块设计复杂度,用来衡量模块判定结构,即模块和其他模块的调用关系。软件模块设计复杂度高意味模块耦合度高,这将导致模块难于隔离、维护和复用。

(3)v(G): 循环复杂度,用来衡量一个模块判定结构的复杂程度,数量上表现为独立路径的条数,即合理的预防错误所需测试的最少路径条数。

参考博客链接:https://www.cnblogs.com/qianmianyu/p/8698557.html

第一次作业

(1)类图

(2)方法度量

(3)分析

1.类图分析

第一次作业时只构建了一个类,运用了多个方法,通过不同的方法来处理输入并输出导数。使得这个类的长度十分的长,结构也不清晰,缺乏面向对象的思维。改进方法:建造两个类,分别处理多项式和单项。多项式类完成对输入字符串判断合法性、接受每个项的求导结果并组合在一起打印出去的功能,单项类的功能是接受输入的单项并提取出系数、指数进行求导、之后将导数结果返回给多项式类。其中系数和指数可以通过二元组共同储存,方便对应关系进行合并提取。

2.复杂度分析

根据结构分析可知,由于将所有的功能处理都放在了main类里,导致main的各个复杂度都十分高,显得结构十分不合理,将其功能分成另外两个类来处理效果会好一点。同时在对合法性判断时,基本复杂度较高,原因在于出现了大量的if/else和while循环,可以考虑研讨课时大佬提出的独占性匹配,使划分更加准确。求导的模块设计复杂度比较高,由于多次调用了其他方法,耦合度较高,可以考虑分类改进后,调用方法数减少来减低复杂度。

第二次作业

(1)类图

(2)方法度量

(3)分析

1.类图分析

这一次的作业开始尝试使用面向对象的思维,构建多个类,结构相较于第一次作业更加的清晰,分别处理多项式、单项式求导、对结果的合并操作以及最后的判断合法性。但缺点也十分突出,依旧没有将输入用类的形式实例化,由此复用性很差,当出现更复杂的表达式(例如第三次)时,便需要重构。

2.复杂度分析

这次的代码,基本复杂度相对来说都不太高,程序模块化和结构化方面做得比上一次作业好得多,而有几个方法的模块复杂度较高,在其中依旧还沿用了面向过程的思想,反复调用一些过程性的方法。而在圈复杂度方面,依旧还是if/else和while循环嵌套层数和个数较多,还是可以巧妙改善正则表达式的匹配模式进行优化代码,使划分更加干净利落。

(1)类图

(2)方法度量

(3)分析

1. 类图分析

本次作业的结构更加优于第二次作业。由于递归的需求,层次之间需要更分明的划分,因此这次的代码结构十分清晰,但遗憾的是,由于时间关系没有用更好的建立公共因子类并采用继承-接口形式,可能结构会更加清晰好看。

2. 复杂度分析

这一次的代码复杂度相比于前两次都高了很多,其主要原因是while循环,if/else式的结构出现次数更多,以及由于递归不同类之间反复调用传递参数进行求导。

四、to hack or not to hack

三次作业的互测环节,均是拿一些自己构造的样例对某一个或某两个人的代码进行检测,遇到错误则提交。尽量保持hack的次数不要太多。

第一次作业

构造样例时,优先考虑了首项出现符号或空格错误、合并同类项后约掉的情况、空串、会出现爆栈的情况、判断/f或/v以及在所有可能加空格的地方随机插入空格的错误。在判断计算是否正确的情况中,构造一些系数或指数较大且含复杂正负号及前置0的情况。

第二次作业

样例构造方法与第一次类似,同时加入对sin(x)哪里可以加空格哪里不能加空格的错误样例。

第三次作业

这次作业较为复杂,可根据自己写代码时遇到的bug来检验其他人的代码。

五、感想

面向对象思维真的特别重要!×3,无论什么时候,在java语言里巧妙地通过类架构可以让代码变得结构清晰易懂,同一接口的类中的同一方法均可被无差别调用,大大的简化了重复的步骤,使代码更加简洁明了。同时,当发现自己的代码结构混乱时,一定不要急于完成,理清好思路再重新构建,否则将会面对代码像马蜂窝一样,debug时常常是改了这点动了那点,触一发而动其身。

OO第一单元作业总结之初识面向对象的更多相关文章

  1. 【OO学习】OO第一单元作业总结

    OO第一单元作业总结 在第一单元作业中,我们只做了一件事情:求导,对多项式求导,对带三角函数的表达式求导,对有括号嵌套的表达式求导.作业难度依次递增,让我们熟悉面向对象编程方法,开始从面向过程向面向对 ...

  2. OO第一单元作业总结——表达式求导

    OO第一单元作业总结 第一次作业 基于度量分析代码结构 基本算法 第一次作业是简单多项式导函数求解,不需要对输入数据的合法性进行判定, 基本思想是用 (coeff, expo)表示二元组 coeff* ...

  3. 【作业1.0】OO第一单元作业总结

    OO第一单元作业已全部完成,为了使这一单元的作业能够收获更多一点,我回忆起我曾经在计算机组成课设中,经常我们会写一些实验报告,经常以此对实验内容反思总结.在我们开始下一单元的作业之前,我在此对OO第一 ...

  4. OO第一单元作业总结

    oo第一单元的作业是对多项式的求导.下面就是对三次作业分别进行分析. 第一次作业 分析 第一次作业相对来讲比较简单,甚至不用面向对象的思想都能十分轻松的完成(实际上自己就没有使用),包含的内容只有常数 ...

  5. 北航OO第一单元作业总结(1.1~1.3)

    经过了三次作业之后,OO第一单元告一段落,作为一个蒟蒻,我初步了解了面向对象的编程思想,并将所学内容用于实践. 一.第一次作业 1.架构分析 本次作业需要完成的任务为简单多项式导函数的求解.表达式仅支 ...

  6. OO第一单元作业小结

    前言 第一单元的主题是表达式求导,第一次作业是只带有常数和幂函数的求导,第二次作业加入了正余弦函数,第三次作业又加入了表达式嵌套,难度逐渐提升.总体来说前两次作业还易于应对,而第三次作业做得相对有些艰 ...

  7. 2019北航OO第一单元作业总结

    一.前三次作业内容分析总结 前言 前三次作业,我提交了三次,但是有效作业只有两次,最后一次作业没能实现多项式求导的基本功能因此无疾而终,反思留给后文再续,首先我介绍一下这三次作业,三次作业围绕着多项式 ...

  8. OO第一单元作业——魔幻求导

    简介 本单元作业分为三次 第一次作业:需要完成的任务为简单多项式导函数的求解. 第二次作业:需要完成的任务为包含简单幂函数和简单正余弦函数的导函数的求解. 第三次作业:需要完成的任务为包含简单幂函数和 ...

  9. 多项式与三角函数求导——BUAA OO 第一单元作业总结

    第一次作业 需求简要说明 针对符合规定的多项式表达式输出其符合格式规定的导函数多项式,格式错误输出WRONG FORMAT! 带符号整数 支持前导0的带符号整数,符号可省略,如: +02.-16> ...

随机推荐

  1. [MySQL] timestamp和datetime的区别

    建表语句如下: create table strong_passwd_whitelist( id int unsigned not null auto_increment, email_id int ...

  2. Java核心知识盘点(二)- 缓存使用

    Redis有哪些数据类型 String.Hash.List.Set.ZSet String:字符串 Hash:哈希 Set:集合 List:列表 ZSet:有序集合 Redis内部结构 1.Redis ...

  3. js 整理

    类型 1.js 中的数据类型,解释清楚原始数据类型和引用数据类型 js中共有null, underfined, string, number, boolean, object 六种数据类型 原始数据类 ...

  4. 关于Ocelot和Consul 实现GateWay(网关) 服务注册 负载均衡等方面

    Ocelot   路由  请求聚合  服务发现 认证  鉴权 限流熔断 内置负载均衡器 Consul   自动服务发现    健康检查 通过Ocelot搭建API网关   服务注册   负载均衡 1. ...

  5. vue_全局注册过滤器

    在一个项目中, 某些过滤器全局都有可能用的到, 统一管理并自动化全局注册是很方便的. 代码如下, 后续只需要在src/filters/index.js中添加方法就可以全局使用过滤器了. // src/ ...

  6. EM公式推导

    纯手写,字很丑,人也很丑.. E步公式是怎么来的呢?推导步骤如下, EM算法核心思想是先给定初始θ,求样本X,和隐变量z的期望(实际上是个函数),可以画一个曲线,M步:然后不断滑动θ,找到使得期望最大 ...

  7. VSCode中使用vue项目ESlint验证配置

    如果在一个大型项目中会有多个人一起去开发,为了使每个人写的代码格式都保持一致,就需要借助软件去帮我们保存文件的时候,自己格式化代码 解决办法:vscode软件下载一个ESLint,在到设置里面找到se ...

  8. Kali 安装 VMwaretools 时 “没有足够可有空间提取xxxxxx”

    方法:将VMwaretools 的压缩包复制到想要解压的地方,然后再进行提取

  9. Asp.Net MVC+EF+三层架构

    架构图: 使用的数据库: 一张公司的员工信息表,测试数据 解决方案项目设计: 1.新建一个空白解决方案名称为Company 2.在该解决方案下,新建解决方案文件夹(UI,BLL,DAL,Model) ...

  10. Java 面向对象 知识点基础浅谈

    1.类和对象的关系 类是一个抽象的模板,对象是根据模板制造出来的,只有类建立之后,对象才可以在类中实例化对象.举个例子讲:我要用黄金浇筑一块砖,我会在一个模型里进行,这样才能有砖的形状,那模型即是类, ...