其他分享
首页 > 其他分享> > oo第一单元总结

oo第一单元总结

作者:互联网

为期三周的oo的unit1终于过去了,在这三周中在oo上付出的时间是最多的,同时收获也是最多的。现在来总结一下这个单元。

最终问题描述

基本思路

题目可以简述为输入一个表达式,输出化简结果。因此我们基本的思路可以是:

<div align = "center">

读入预处理存储并解析简化输出

</div>

预处理

这一部分首先就是去空白字符、将**转化为~等基本操作便于之后操作。之后做了两件比较重要的操作——化简加减号和表达式的替换。

加减号的问题在第一次作业中十分困扰我,因为它既可以作为一个运算符,又可以作为因子、项、表达式的正负号,这种双重属性较难处理。我所了解的大概有两种方法:一,把重复的++、+-、-+、*+、(+等等全部替换,例如:++变为+、+-变为-、*+变为*;二,在解析的时候识别出到底是表示正负还是加减。我选择了看似简单但坑很多的第一种,因为有的情况考虑不全面,所以替换不完全,直到hw2的时候仍然发现了这方面的问题。

表达式替换可以说是我这次作业中让自己最不满意的地方了,虽然助教老师多次推荐表达式解析,而且表达式解析的实现也并不复杂,但由于我自己比较懒,只想用自己熟悉的方法,所以选择了字符串替换。其实字符串替换在这次作业来说并不容易出错,只要替换的时候注意顺序,多加括号并不会出现方法上的问题,但是从可扩展性和逻辑上来说不够优秀。

存储并解析

这部分是整个题目中最重要的部分,在我的设计中,这两部分是完全分开设计的,存储部分提供容器和方法,解析部分利用容器来存储和运算表达式。

存储

我们读入表达式之后肯定要选择一种存储方式,因为字符串又不能进行加减乘等运算,所以我们的目标在于找到一种可以在其上定义加、减、乘、指数运算的存储方式。

第一次作业中,由于所有能出现的形式都可以表示为多项式,因此一个HashMap<Integer,Integer>就可以很轻松的存储这些表达式,也很好定义计算。

第二次作业中,由于出现了三角函数,之前的存储方式不能满足。由于规定了三角函数内部只能是常数或幂函数,所以我当时是以一种比较傻的方式来存储——先定义一个Tri表示三角函数内部可能出现的情况,然后定义了一个包含一个index(指数)+ 两个HashMap<Tri,Integer>的Storage类,最终定义了一个包含一个HashMap<Storage,Integer>的Container类。这么做有一个极大的问题就是当下一次三角函数里面不确定是什么的时候这种方法就失效了,可扩展性极差。

第三次作业中,我仍然想采用HashMap的存储方法,由于三角函数中的表达式不定(可以多层嵌套),因此第二次的存储方式显然不行。这一次构建了一个内部类(Inner),其中包含一个HashMap<String, Integer>,它的作用是:可以表示任意(String)**(Integer)的形式的式子,由于我们使用了String,因此无论是x、sin(x)、cos(1),还是是多层嵌套,例如:sin(sin(cos((x**2+3))))在Inner看来都是一样的,他们都只是一个字符串而已。然后依旧是定义了一个包含一个HashMap<Inner,BigInteger>的Container类并在其上定义了基本的运算。

解析

解析部分的思路是递归下降。这一部分从第一次作业开始架构基本就没有改变,因为无论如何表达式形式如何,都只需要把当前操作因子提取出来,然后递归下降,下降到底层后再开始一步步返回,已有的运算顺序不变,当加入新的运算操作时只需要把它加到该它运算的那一层。流程如图所示:

简化

在第一次作业中,由于只有多项式,所以只需要合并同类项,这个操作其实在add和sub方法中已经实现了。

在第二次作业中,我并没有简化三角。

在第三次作业中,我针对sin()**2 + cos()**2、2*sin()*cos()、sin(0)、cos(0)进行了化简,当时也没问别人怎么做的,也没看往届博客,于是把sin(-x)和cos(-x)这两种形式忘了,导致有的数据性能分直接为0。

输出

前两次作业的输出都是比较蠢的方法——在主函数中遍历,最后一次作业中,我选择把输出的任务分配到每一个存储的类中,Inner可以输出inner所表示的信息,Container调用Inner的toString输出container所表示的信息,主函数中只需要调用Container的toString即可。

程序结构的分析

由于三次迭代开发过程中,解析部分基本没变,存储部分的区别在上面已经介绍,因此程序结构的部分只针对最后一版代码来分析。

总架构

其中Container和Inner是实现了数据的存储和数据间的运算,在上一部分中已经介绍。

Parser和Lexer实现了递归下降文法解析,Lexer提供当前操作因子,Parser通过调用Lexer获取当前操作因子然后递归下降。在Parser类中还实现了一部分三角化简的功能,即simplifyexpr和simplifyterm,这两个方法分别化简了sin()**2 + cos()**2和2*sin()*cos()两种形式。

Function的功能是分析输入的自定义函数,并且在最终表达式中将自定义函数替换。

规模分析

从这个数据来看,我们可以发现Parser类最冗长,其实导致这个的原因是:Parser内部不仅实现了递归下降,还在递归下降的过程中实现了一部分化简。其次Container类也比较长,这个类从设计上其实是不合理的,因为这个类承担了太多职责,这次作业中的所有计算操作都定义在这个类上。主要体现出来的问题是在设计时没有考虑单一职能原则,导致类的内部有些复杂。

复杂度分析

ClassOCavgOCmaxWMC
Function 9.5 15 19
Lexer 2.4 5 12
Main 6 12 18
Parser 6.14 15 43
storage.Container 3.38 12 44
storage.Inner 2.36 5 33

 

MethodCogCev(G)iv(G)v(G)
Function.parse(String) 5 1 4 4
Function.replaceAll(String) 48 7 14 16
Lexer.Lexer(String) 0 1 1 1
Lexer.getFactor() 3 1 5 5
Lexer.getTri() 4 3 3 4
Lexer.next() 6 2 6 8
Lexer.peek() 0 1 1 1
Main.main(String[]) 4 1 4 4
Main.pre(String) 2 1 3 3
Main.replaceAllsum(String) 36 7 13 15
Parser.Parser(Lexer) 0 1 1 1
Parser.parseExpr() 1 1 2 2
Parser.parseFactor() 13 5 7 7
Parser.parsePow() 1 1 2 2
Parser.parseTerm() 1 1 2 2
Parser.simplifyexpr(Container) 66 11 17 20
Parser.simplifyterm(Container) 78 11 17 21
storage.Container.Container() 0 1 1 1
storage.Container.Container(Inner, BigInteger) 0 1 1 1
storage.Container.add(Container) 4 1 3 3
storage.Container.addvalue(Inner, BigInteger) 0 1 1 1
storage.Container.containersimplify() 3 1 3 3
storage.Container.cos(Container) 5 1 3 3
storage.Container.getSourceHashmap() 0 1 1 1
storage.Container.isexpr(String) 17 4 10 14
storage.Container.mul(Container) 7 1 4 4
storage.Container.pow(Container) 3 1 3 3
storage.Container.sin(Container) 4 1 3 3
storage.Container.sub(Container) 0 1 1 1
storage.Container.toString() 35 1 12 12
storage.Inner.Inner() 0 1 1 1
storage.Inner.Inner(String, Integer) 0 1 1 1
storage.Inner.abs() 4 1 3 3
storage.Inner.add(Inner) 5 1 4 4
storage.Inner.addvalue(String, Integer) 0 1 1 1
storage.Inner.divide() 1 1 2 2
storage.Inner.equals(Object) 3 3 2 4
storage.Inner.getSourceInner() 0 1 1 1
storage.Inner.hashCode() 0 1 1 1
storage.Inner.innersimplify() 3 1 3 3
storage.Inner.mul(Inner) 7 1 5 5
storage.Inner.numofinner() 0 1 1 1
storage.Inner.sub(Inner) 5 1 4 4
storage.Inner.toString() 4 1 3 3

附:度量名词解析

BUG与HACK

BUG

bug出现的主要原因是一些特殊的、边界情况没有考虑,而由于没有写自动评测机,因此自己在造测试数据时不够全面,导致出现了一些特殊的bug。程序的鲁棒性不够高。

同时我们还可以发现出现bug的类代码行数和圈复杂度比较高,因此之后的架构设计中,应该尽量降低类的代码行数和圈复杂度。

HACK

由于并没有写自动评测机,因此hack时只能是采用阅读他人代码来找逻辑上的漏洞,然而事实上这种方法不仅费时,还低效,很难找出某一个错误。

架构设计体验

架构设计从第一版开始就确定了,主要实现:递归下降文法解析+数据存储与计算。递归下降文法解析部分在三次迭代中表现出很强的可扩展性,只需要加入一些情况即可。而数据存储与计算在这三次迭代中虽然主体思路都是找到一个总容器来存储所有数据,但前两次都表现出了很低的可扩展性,每次出现新的要求都需要重构自己的代码。不过在最后一次,找到了一种合理的、可扩展性很强的存储方式。

心得体会

标签:oo,总结,Container,表达式,storage,空白,因子,Inner,单元
来源: https://www.cnblogs.com/pc-hao/p/16053156.html