OO2022第一单元个人总结

第一单元的主要任务是通过字符串操作实现表达式的化简。通过本单元的学习,我们熟悉了以面向对象的思维方式思考问题,同时我们也初步体会了层次化设计的思想,并拥有了通过可变长容器,正则表达式等手段拥有了更加灵活的处理字符串的手段。在第一次作业中,我们实现了化简表达式的基本框架;第二次作业中,实现了三角函数与函数的调用;第三次实验中,实现了嵌套括号的处理。以下将分别进行介绍。

1.第一次作业

第一次作业的重点在于构建树形结构,通过将构建表达式类转化为构建多个项类,再将构建项类转化为构建因子类,从而实现从上到下的表达式->项->因子的树形结构。这种树形结构使得各类之间有了鲜明的层次性与递进性,其优越性在于层次化定义和表达式的运算优先级是一一对应的,通过输入->表达式类构造->项类构造->因子类构造->因子类处理->项类处理->表达式类处理->输出的模式,在层次结构中隐含了表达式的运算顺序,能良好的处理表达式的运算。

1.1数据结构

 

在第一次作业构造时,MainClass将输入的字符串进行预处理后(去掉空白符与多余的符号)传给Expr,而Expr处理转入的字符串,将总字符(表达式)串切割为一个又一个的子字符串(项),Term再处理传入的字符串,将总字符(项)串切割为一个又一个的子字符串(因子),Factor再对输入的字符串进行标准化(方便合并同类项,在本次实验中,将所有的单项式化为a*x**b的形式)后,存储在Factor中。由于此时因子的种类较少,可以在一个类中就对传入的字符串进行进行识别并处理。此时可以借助StringHanding中的多项式乘多项式的方法(polyMulPoly)进行格式的标准化,在polyMulPoly中,将输入的字符串变为多个单项式乘单项式(monMulMon),将monMulMon的返回值设置为a*x**b的格式即可实现标准化。所以将每个Factor中的字符串利用polyMulPoly乘“1”就可以实现标准化。

在需要获取结果时,MainClass调用Expr实例化对象的calulate()方法,Expr的calulate()方法会去调用它之下的每个Term的calulate()方法并把放回的结果相加,进行拼接,而Term的calulate()方法也会把它之下的所有Factor中存储的内容相乘,从而的到相乘化简后的结果并将其放回到Expr的calulate()方法中,从而实现树形递归,得到最后的结果。在MainClass中调用merge方法对得到的结果进行合并同类项即可得到最终结果。

值得一提的时,StringHanding是一个字符串处理的工具类,其中存放了字符串处理的各种静态方法,可以在树形结构的各级层次中被调用,以减小处理字符串的难度。

第一次作业难度较低,但是要求设计出一个合理的框架,方便后续作业进行相应的扩展。

1.2代码结构分析

MethodCogCev(G)iv(G)v(G)
Expr.Expr() 0 1 1 1
Expr.Expr(String) 1 1 2 2
Expr.calculate() 1 1 2 2
Expr.getItemArray() 0 1 1 1
Factor.Factor() 0 1 1 1
Factor.Factor(String, int) 4 1 4 6
Factor.getStr() 0 1 1 1
Factor.getType() 0 1 1 1
Factor.minMal(String, int) 3 2 3 4
MainClass.main(String[]) 3 1 2 3
StringHanding.addSym(String) 3 1 2 3
StringHanding.equalBig(BigInteger, int) 3 3 3 3
StringHanding.getArray(String, String, ArrayList<String>) 1 1 2 2
StringHanding.getItem(BigInteger[], String) 4 1 4 4
StringHanding.merge(String) 25 5 10 12
StringHanding.monMulMon(String, String) 7 1 5 6
StringHanding.polyMulPoly(String, String) 4 1 3 4
StringHanding.removeBlank(String) 4 1 4 4
StringHanding.removePlusSymbol(String) 0 1 1 1
Term.Term() 0 1 1 1
Term.Term(String) 7 1 5 6
Term.calculate() 2 1 3 3
Term.getStr() 0 1 1 1
Total 72.0 30.0 62.0 72.0
Average 3.130434782608696 1.3043478260869565 2.6956521739130435 3.130434782608696

本次作业中,对于合并同类项的方法merge较为复杂(同时实现了字符串输出的简化),导致了整体的复杂度偏高,并且缺少并要的注释行说明。但整体看来,还是比较良好,并且为后续的扩展提供了基础,改良起来较为简单。

1.3bug修复

在第一次实验中,强测时未检测出bug,但在互测时,却被检测出来bug,并且就这一个bug就被hack了14次:😭,这个bug就是多余符号处理不得当。在第一次实验中,我仅仅在预处理阶段消除了多余的符号,但却没有考虑到在多项式乘多项式后仍有可能会产生多余的符号。而改进的方法也比较简单,就是对于多项式乘多项式的结果经过处理多余符号再返回。·

2.第二次作业

第二次作业在第一次作业的基础上引入了三角函数与函数调用,同时也引入了更多种类的因子。对于所以在第二次作业中,我引入因子类共同的子类Factory,因子类都实现这一接口,并且拥有自己的自展开方法。值得一提的是本次作业的结构相比于上一次有了一定程度上的改变,树形结构已然不是单纯的表达式->项->因子,本次的因子类共有6中(常数因子,幂函数因子,表达式因子,三角函数因子,自定义函数因子,求和函数因子),他们的层次是不同的。就化简后而言,表达式因子,自定义函数因子,求和函数因子应该不复存在(通过自展开递归变为常数因子,幂函数因子,三角函数因子),而常数因子,幂函数因子,三角函数因子(作为递归的出口)仍可保留,所以此次的结构应该是表达式->项(->表达式因子,自定义函数因子,求和函数因子)->常数因子,幂函数因子,三角函数因子。

2.1数据结构

 

第二次作业在第一次作业的基础上引入了三种新类型的因子,随之而来的也带来了几个难点,1)首先便是字符串的拆分已经不能像第一次一样再通过正则表达式的抓取了,这是因为自定义函数因子,求和函数因子的替换其实已经在广义上引入了括号的嵌套,此时字符串的分割需要通过Point方法截断不再括号内的 "+" 或"*" 来实现。2)其次便是合并同类项依赖的单项式的标准化,在上一次作业中可以通过a*x**b的形式来轻松实现,但这一次作业引入了三角函数,每一个单项式显然没有共同的标准化形式。但我们可以考虑到对于合并同类项是对系数进行的操作改变,如果把一个单项式分为系数部分与非系数部分,把非系数部分相同的单项式的系数合并即可,此时我们便需要考虑要将类似sin(x)*cos(x)与cos(x)*sin(x)化为相同的形式,使得其有特定的顺序。所以在这一次作业中我引入了box类。对于多项式乘多项式,我将其转化为单项式乘单项式;而对于单项式乘单项式,我有用 "*"将其拆分为一个个基础因子(常数因子,幂函数因子,三角函数子),再将其逐个加入到box中,其中coefficient会统计系数,index会统计x的指数,而hsb是一张以三角函数为键,其相应指数为值的hashmap,若新加入的三角函数已存在与HashMap中则会更新指数,若以存在则会创建新的键值对。最后将HashMap按遍历顺序输出,就能得到特定顺序的字符串。3)便是因子层次上的构析,对于表达式因子,自定义函数因子,求和函数因子,我们想将其化为常数因子,幂函数因子,三角函数因子,这是我们便可调用他们各自的unfold方法,对于Expr类,我们可以通new Expr(s).calculate()来获得s化简后的字符串,对于表达式因子,我们同样可以对它去掉最外层括号后的字符串s1使用new Expr(s1) . calculate()来获得化简后字符,自定义函数因子,求和函数因子对替换后的字符串s2也可通过new Expr(s2).calculate()来实现化简。此时常数因子,幂函数因子,三角函数因子则作为递归调用的出口,从而使得逐层递归得以实现。

在本次作业获取结果时,也是和上一次相同,通过调用new Expr(s).calculate()就可以实现。

本次作业相比于上次作业,在化简合并上的难度大大增加,此时我也没有考虑合并同类项之外的三角函数化简。但总体来看,本次作业的结构较为清晰,思路也比较简单,使得下一次作业的修改得到了大大的简化。

2.2代码结构分析

MethodCogCev(G)iv(G)v(G)
Box.Box() 0 1 1 1
Box.getStr() 8 3 5 6
Box.mul(String) 15 1 9 9
Constant.Constant(String) 0 1 1 1
CustomFunction.CustomFunction() 0 1 1 1
CustomFunction.CustomFunction(String) 1 1 2 2
CustomFunction.callFunction(String) 4 1 5 5
CustomFunction.getName() 0 1 1 1
Expr.Expr() 0 1 1 1
Expr.Expr(String) 9 1 7 7
Expr.calculate() 1 1 2 2
ExprFactor.ExprFactor(String) 0 1 1 1
ExprFactor.unfold(String) 4 3 3 4
Factor.Factor() 0 1 1 1
Factor.Factor(String) 0 1 1 1
Factor.getStr() 0 1 1 1
Factor.setStr(String) 0 1 1 1
FunFactor.FunFactor(String) 0 1 1 1
FunFactor.unfold(String) 3 1 3 3
MainClass.main(String[]) 1 1 2 2
Point.Point() 0 1 1 1
Point.getLayer() 0 1 1 1
Point.getPos() 0 1 1 1
Point.setLayer(int) 0 1 1 1
Point.setPos(int) 0 1 1 1
PowerFactor.PowerFactor(String) 0 1 1 1
PowerFactor.unfold(String) 3 3 2 3
StringHandling.addSym(String) 3 1 2 3
StringHandling.filter(String) 0 1 1 1
StringHandling.getAc() 0 1 1 1
StringHandling.merge(String) 26 6 11 12
StringHandling.monMulMon(String, String) 3 1 2 3
StringHandling.polyMulPoly(String, String) 3 1 3 3
StringHandling.removeBlank(String) 4 1 4 4
StringHandling.removePlusSymbol(String) 0 1 1 1
StringHandling.setAc(ArrayList<CustomFunction>) 0 1 1 1
StringHandling.splitAdd(String) 9 1 7 7
StringHandling.splitMul(String, ArrayList<String>) 8 1 6 6
StringHandling.strSplit(String, int) 0 1 1 1
SumFactor.SumFactor(String) 0 1 1 1
SumFactor.unfold(String) 4 3 3 5
Term.Term() 0 1 1 1
Term.Term(String) 8 1 6 6
Term.calculate() 2 1 3 3
Term.getFactor(String) 6 1 7 7
TriangleFactor.TriangleFactor(String) 0 1 1 1
TriangleFactor.unfold(String) 12 8 6 8
Total 137.0 67.0 125.0 135.0
Average 2.9148936170212765 1.425531914893617 2.6595744680851063 2.872340425531915

因为写代码是并没有怎么在意,使得merge,box.mul等实现核心功能的函数复杂度还是较高。总的来将这一单元因为初学,写的代码还是比较烂的,下一次写代码时也要主动降低耦合度了🤔

2.3bug修复

本次作业因为提交之后就没有再看了,导致代码出现了两个bug。1)沿用了上一次代码的变号规则,即以符号开头的多项式,要把"+"变为"-","-"变为"+",但在这次的三角中,sin(-1)变为sin(+1)就gameover了😭。改进方法也较为简单,就是使用Point类,只改变括号外的"+"与"-"。2)函数替换后,要在两边加括号,即f(x)=x**2,f(x**1),如果替换为x**1**2就会导致错误,应该为(x**1)**2。改进是只需在替换字符串两边加括号即可。

3.第三次作业

我的第三次作业完全沿用了第二次作业的方法。仅仅是三角函数因子也调用了递归,将常数因子,幂函数因子设置为出口即可。

3.1数据结构

 

第三次作业使用表达式->项(->表达式因子,自定义函数因子,求和函数因子,三角函数因子)->常数因子,幂函数因子的结构形式进行解析。由于沿用上一次的方法就可以完成基础任务,所以这一次将重心放到了合并三角函数上。

1)通过将sin(x)**2都转化为(1-cos(x)**2)的方法解决sin(x)**2+cos(x)**2=1的情况。

2)通过遇到cos(x)**2我就把他变为(1-sin(x)**2),再跑一遍输出代码,看新输出rival与原输出s谁更短,如果新输出更短,则把min变为rival,并标记flag已发生改变,再重复执行,直到更改完成每一个cos(x)**2都不会使得输出更短,则得到了最短输出。解决sin(x)**2=1-cos(x)**2变长的弊端。

3)通过在box类中设置新的方法,合并2*sin(x)*cos(x)=sin(2*x)的情况

3.2代码结构分析

MethodCogCev(G)iv(G)v(G)
Box.Box() 0 1 1 1
Box.getStr() 7 3 4 5
Box.getStr(boolean) 40 7 11 15
Box.mul(String) 15 1 9 9
Constant.Constant(String) 0 1 1 1
CustomFunction.CustomFunction() 0 1 1 1
CustomFunction.CustomFunction(String) 1 1 2 2
CustomFunction.callFunction(String) 4 1 5 5
CustomFunction.getName() 0 1 1 1
Expr.Expr() 0 1 1 1
Expr.Expr(String) 9 1 7 7
Expr.calculate() 1 1 2 2
ExprFactor.ExprFactor(String) 0 1 1 1
ExprFactor.unfold(String) 4 3 3 4
Factor.Factor() 0 1 1 1
Factor.Factor(String) 0 1 1 1
Factor.getStr() 0 1 1 1
Factor.setStr(String) 0 1 1 1
FunFactor.FunFactor(String) 0 1 1 1
FunFactor.unfold(String) 3 1 3 3
MainClass.main(String[]) 1 1 2 2
Point.Point() 0 1 1 1
Point.getLayer() 0 1 1 1
Point.getPos() 0 1 1 1
Point.setLayer(int) 0 1 1 1
Point.setPos(int) 0 1 1 1
PowerFactor.PowerFactor(String) 0 1 1 1
PowerFactor.unfold(String) 3 3 2 3
StringHanding.addSym(String) 3 1 2 3
StringHanding.cut(String, int) 20 5 9 13
StringHanding.desSin(String) 10 5 4 5
StringHanding.filter(String) 0 1 1 1
StringHanding.getAc() 0 1 1 1
StringHanding.isComplex(String) 1 2 1 2
StringHanding.merge(String) 26 6 11 12
StringHanding.monMulMon(String, String) 3 1 2 3
StringHanding.polyMulPoly(String, String) 3 1 3 3
StringHanding.reSin(String, int) 17 5 9 12
StringHanding.removeBlank(String) 4 1 4 4
StringHanding.removePlusSymbol(String) 0 1 1 1
StringHanding.setAc(ArrayList<CustomFunction>) 0 1 1 1
StringHanding.shorter(String) 10 5 4 5
StringHanding.sinMulCos(String) 3 1 3 3
StringHanding.splitAdd(String) 9 1 7 7
StringHanding.splitComma(String) 8 1 6 6
StringHanding.splitMul(String, ArrayList<String>) 8 1 6 6
StringHanding.strSplit(String, int) 0 1 1 1
SumFactor.SumFactor(String) 0 1 1 1
SumFactor.unfold(String) 3 3 2 4
Term.Term() 0 1 1 1
Term.Term(String) 8 1 6 6
Term.calculate() 17 1 10 10
Term.getFactor(String) 6 1 7 7
TriangleFactor.TriangleFactor(String) 0 1 1 1
TriangleFactor.unfold(String) 8 6 3 6
Total 255.0 96.0 174.0 199.0
Average 4.636363636363637 1.7454545454545454 3.1636363636363636 3.618181818181818

这一次的三角函数合并方法并不好,所以导致复杂度过高。助教推荐的合并方法为对于A*sin(X)**2+B*cos(X)**2+C 可以直接推出一个关于 A,B,C 的化简公式。

3.3bug分析

这一次幸运的在强测与互测中都没有被找出bug,但发现身边的同学有很多都犯了一个错误,就是对于求和函数替换时要将替换的内容要加括号。上次作业我在自定义函数替换中出现了这种错误😏

4.bug分析

个人感觉由于我们的作业是递进式作业,所以再进行扩展后常常会出现以前的方法会出现问题的情况,所以在我们写完之后,我们应该回去看一下以前的结构是否会出现问题。对于写代码时经常出现的重复性代码,个人感觉可以将其抽离出来,从而使得代码结构更加清晰。而且dbug时要求一个bug不能超过5行,我们调用函数甚者可以在5行内解决多个bug🌝

5.hack策略

由于时间原因,个人并没有怎么参加hack,因为没有时间去分析同学们的白盒代码,就直接随意写了几个数据。在我的hack经验中,并不是越复杂的数据越容易打中人(复杂的数据一般还发不出去),就我打中人的几次数据(-sin(-x),(1)**100,sum(i,10,10,i**1)都是再简单不过的数据。所以我觉得用简单的数据覆盖更多的基本面就可以了。

6.架构设计体验

在第一次作业时,我大半的时间都拿去思考架构设计。首先我是通过去看本单元的训练栏目,并在纸上画出来自己的大致框架,之后的作业都是在此基础上进行的延展,但在第一次作业中我使用的一次性解析到底,没有引入递归的概念。在第二次作业中,由于有了三角函数,自定义函数,求和函数等广义上的括号嵌套,就使得第一次框架的实现方式(正则表达式解析)不再适用,复杂因子的相互嵌套(表达式因子里有自定义函数等)使得一次性解析到底难以实现,最终我的代码迎来了一次大重构,引入了因子之间的继承与解析时的相互调。在这样的模式下,我第三次作业几乎没怎么改就完成了。所以个人觉得框架的设计真是一件非常重要的事情。

7.心得体会

感觉OO一上来就有点难呀。关键是直接默认我们有基础😭,不过还是要感谢pre,不让我可能就直接寸步难行了。第一次以面向对象的角度思考问题,感觉自己还是带有许多面向过程的思维模式。第一次写java代码,感觉自己各个类之间的耦合度还是挺高的,下次写时一定要注意降低耦合度。字符串处理上感觉自己并没有用到hashcode,迭代器之类的就搞定了,总觉得我这单元的东西没有学全呀。

 

 

posted on 2022-03-23 21:53  计组战力单位  阅读(34)  评论(1编辑  收藏  举报