OO_BUAA_Unit1

2024-01-15 01:59
文章标签 unit1 oo buaa

本文主要是介绍OO_BUAA_Unit1,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

第一单元_表达式解析

前言

本次作业要求对一个表达式进行化简,之后增加了对三角函数,嵌套括号,自定义函数以及求导的迭代。

homework1

设计思路

本次作业我使用了递归下降法进行对表达式的解析,首先通过使用Preteat类对表达式进行预处理,通过对层层解析,当解析得到某个变量对象后使用Expr.addExpr(Factor)Term.addTerm(Factor)将其返回到其父类。最后通过Output类进行多项式的输出。

递归下降法分析表达式主要是通过Parser(语法分析器)与Lexer(词法分析器)进行逐个字符的解析,其与常规的整体分析表达式的区别在于边读边解析,解决了递归层数较多时整体分析法容易陷入混乱的问题

public class Parser {public Expr parserExpr() {Expr expr = new Expr();expr.addTerm(parserTerm());while (lexer.getCurToken().equals("+") || lexer.getCurToken().equals("-")) {expr.addTerm(parserTerm());}return expr;}public Term parserTerm() {Term term = new Term();Factor factor = parserFactor();term.addFactor(factor);while (lexer.getCurToken().equals("*")) {lexer.next();term.addFactor(parserFactor());}return term;}public Factor parserFactor() {if (lexer.getCurToken().equals("(")) {//解析Expr因子} else {Num num = lexer.getNum();Pow pow = lexer.getPow();if (num != null) {//解析Num因子} else if (pow != null) {//解析Pow因子} else {return null;}}}
}

除此之外,本次作业另外存在的一个难点在于对变量类的构造,我大约想出了以下几点以适应我的思路。

pp8UVEV.png

  • 对于变量的分类,本次作业的主要变量类型有Expr,TermFactor三种类型,其中Factor有三种类型Num(常数因子),Pow(幂函数因子)与Expr(表达式因子),故自然想到了利用接口interface Factor来实现变量类。

  • 对于变量类的具体实现,关键在于变量类的数据结构。

对于Pow

P o w = x i n d e x 1 ∗ y i n d e x 2 ∗ z i n d e x 3 Pow = x^{index_1}*y^{index_2}*z^{index_3} Pow=xindex1yindex2zindex3

对于Num

N u m = n u m ( n u m ! = 0 ) Num = num(num != 0) Num=num(num!=0)

对于Expr(表达式),Term(项)的本质分析,本质上均为

E x p r = ∑ x i n d e x 1 ∗ y i n d e x 2 ∗ z i n d e x 3 ∗ n u m = ∑ P o w + ∑ N u m = ∑ B i g I n t e g e r ∗ F a c t o r Expr = \sum x^{index_1}*y^{index_2}*z^{index_3}*num = \sum Pow+ \sum Num = \sum BigInteger * Factor Expr=xindex1yindex2zindex3num=Pow+Num=BigIntegerFactor

T e r m = ∑ x i n d e x 1 ∗ y i n d e x 2 ∗ z i n d e x 3 ∗ n u m = ∑ P o w + ∑ N u m = ∑ B i g I n t e g e r ∗ F a c t o r Term = \sum x^{index_1}*y^{index_2}*z^{index_3}*num = \sum Pow + \sum Num = \sum BigInteger * Factor Term=xindex1yindex2zindex3num=Pow+Num=BigIntegerFactor

考虑到上述数学公式,各种变量类的容器也就自然了,我采用的是

public class Num implements Factor {private BigInteger num;}
public class Pow implements Factor {private HashMap<String, BigInteger> hashMap;}
public class Expr implements Factor {private HashMap<Factor, BigInteger> hashMap;//BigInteger是系数}
public class Term implements Factor {private HashMap<Factor, BigInteger> hashMap;//BigInteger是系数}

除此之外,我还设立了Output类用以对各个对象进行字符串输出与化简,Pretreat类用以预处理

大概的几个基本的难点就搞定了,下面是我的类图

pp8UPjs.png

UML图

ppJo5es.png

优化方法

我的优化主要在两个方面:

  • Expr.addExpr(Factor factor)方法中,优先合并同类项,其次再考虑将factor中的PowNum加入到hashmap
  • Output方法作出最终输出时,优先考虑将系数大于0的PowNum输出得,同时将x**2优化为x*x

Bug分析

中强测

由于对HashMap的机制还是不熟悉,在使用迭代器的时候出现了一个bug,在遍历HashMap的时候忘记了及时更改已经变化的值,在较为复杂的计算中会出现系数未被更新的现象,很快就能修好

互测

因为房间等级不高,互测我成功hack了19次,主要是手捏测试数据和阅读源代码发现漏洞,包括但不限于:

  • 在进行复杂式子计算时,系数相加时两个加数有问题

  • 0**0处理不到位

  • 合并同类项时忘记考虑符号

  • 多项式相乘得到的次数系数不正确

体会心得

在第一次作业的过程中我感受到了递归下降法的强大威力,竟然可以轻松的解决复杂式子的分析。同时感受到了一个好的架构往往可以事半功倍,因为第一次作业的框架拓展性还好,后两次作业也没有进行重构,迭代的难度也算不上太大。总之是颇有收获的一次作业。

homework2

迭代要求

本次作业增加了自定义函数和三角函数,文法定义如下

pp8UkBq.png

迭代思路

首先是对自定义函数的解析,这时有两种处理情况,提前解析,之后方便代入,或者先代入,后解析,我采用的是第一种思路,但是由于规则中对不调用的f,g,h函数不计入cost的计算,此时第一种思路在互测可能会出现大问题。但是在大多数情况下还是好用的,我的大概思路是通过设定新的ParserLexer用来解析好表达式,将其存入一个DefinedFunc对象中,在之后对主Expr解析时调用,而对于自定义函数的具体计算我主要采用的时replaceAll()方法,但是如果在函数解析的过程中没有将x,y,z进行替换或者计算时同时将所有形参代入在这样的情况下会出现bug

1
f(x,y) = x + y
f(y,2)
//先代入y,再代入2
f(y,2) -> y + y -> 2+2 = 4
//同时代入
f(y,2) -> y + 2
//在解析的过程中使用其他字母替代
f(p,q) = p + q
f(y,2) -> y+q -> y+2

其次是三角函数的引入,三角函数的基本形式是sin(Factor)/cos(Factor)由于三角函数的引入,除了常规的TrigonoFunc,还会有PowTrigonoFUnc的混合类
M i x t u r e = ∏ sin ⁡ ( F a c t o r ) i n d e x 1 ∗ ∏ cos ⁡ ( F a c t o r ) i n d e x 2 ∗ P o w Mixture = \prod\sin(Factor)^{index1}*\prod\cos(Factor)^{index2}*Pow Mixture=sin(Factor)index1cos(Factor)index2Pow

于是我引入了Mixture类用来记录多项式中的此类型变量,使用容器

public class Mixture implements Factor {private HashMap<TrigonoFunc, BigInteger> hashMap;private Pow pow;}

最后本次作业的新的要求是要求支持嵌套括号,由于第一次作业递归下降的框架,嵌套括号在第一次作业就已经支持,所以这不是问题。

pp8NXHP.png

UML图

ppG39HO.png

优化方法

关于三角函数主要有两种优化方式:合并同类项,和 sin ⁡ ( e x p r ) 2 + cos ⁡ ( e x p r ) 2 = 1 \sin(expr)^2 + \cos(expr)^2 = 1 sin(expr)2+cos(expr)2=1,我只做了第一个优化,但是我注意到由于我在Mixsure类中使用的数据结构是HashMap所以遍历起来很麻烦而且可能有一些多余的同类项没有合并导致我合并同类项没有做到完全合并,而且在优化的过程中发现了重大的bug,优化相当于半途而废,没有完全写完,本次的优化做的并不好。

Bug分析

构造过程的bug

首先就是我在周六下午进行测试时发现的bug,由于框架的可拓展性还好,我的每次迭代只需要大概一下午的时间就可以完成,我的中测和弱测很轻松就通过了,但在周六下午我进行了一些有关于Mixture类的高次(三次及以上)计算,突然发现了巨大的问题,在输出时大量的FactorHashMap都变成了Null,这时我感受到了巨大的压力,此时距离作业提交只剩下不到十八小时,于是我开始大量构造样例去测试,发现我出错的样例缺乏明显的规律而且在越复杂的时候越可能出现bug,于是我对样例进行了长时间的单步调试,发现在exprexpr进行计算时某些无关HashMap发生了变化,此时我感受到了一种可能性——深克隆,在查阅资料之后,我发现在我之前的部分.clone()方法出现了浅克隆的现象,主要是在java自带的HashMap.clone()方法,于是我进行了bug的修复,并再一次进行了较大量的测试,再次完成了提交,此时已经是深夜三四点了(悲)

强测与互测

强测过程中我出现了一个bug,在MixtureMixture乘法时Pow部分的乘法在其中一个为NULL时出现了小问题,在较为复杂的运算时可能出现问题,是我考虑不周而且测试不够的问题,加一行就可以解决。

互测过程中也有一个bug,我在Pretreat方法中的预处理环节对于+++---这两种情况缺乏处理,我在Pretreat方法中加两行就可以解决

找到别人的bug:对sin(0)带前导符号的次幂处理不周。

homework3

迭代思路

这次是我迭代用时最少的一次,也是很寄很寄的一次(还是缺乏测试),主要是对各个变量类增加了public Expr derivative(String s)方法,对于Num类,直接返回Num(0),对于Pow类,也是返回一个简单操作的Pow*Num对象即可,但是我在考虑到复合函数和Mixture求导的情况注意到,返回的对象还是为Expr最好,因为Pow的求导甚至都有系数,而将返回的类型设置为Term没有任何优越性,Term本质上还是Expr的一种子类,拥有其所有性质。而对于乘法法则和嵌套函数的求导,我考虑进行递归下降法解析求导,注意到我的方法几乎随时都在进行拆括号以及化简,除了sin/cos基本不会出现嵌套函数或者乘法原则

下面是对乘法原则的一定程度的还原

public Expr derivativetri(String s) {Mixture mixture1 = new Mixture();mixture1.setHashMap(hashMap2);Mixture mixture2 = new Mixture();mixture2.setHashMap(hashMap1);Term term = new Term();term.addFactor(mixture1.derivative(s));term.addFactor(mixture2);Term term1 = new Term();term1.addFactor(mixture1);term1.addFactor(mixture2.derivative(s));Expr expr = new Expr();expr.addTerm(term);expr.addTerm(term1);return expr;
}

下面是嵌套原则

public Expr derivative(String s) {TrigonoFunc trigonoFunc = new TrigonoFunc();trigonoFunc = this.clone();TrigonoFunc trigonoFunc1 = new TrigonoFunc();trigonoFunc1.setExpr(this.getExpr());if (trigonoFunc.getTri().equals("sin")) {trigonoFunc1.setTri("cos");trigonoFunc1.setFlag(flag);} else {trigonoFunc1.setTri("sin");if (flag.equals("+")) {trigonoFunc1.setFlag("-");} else {trigonoFunc1.setFlag("+");}}Expr expr1 = expr.derivative(s);Term term = new Term();term.addFactor(trigonoFunc1);term.addFactor(expr1);if (!term.judge()) {term.change();}Expr expr2 = new Expr();expr2.addTerm(term);return expr2;}

优化方法

优化的方法和第二次作业基本一致,我没有做多余的优化,为了保证正确性

类图

pp8Up9g.png

UML图与hw2基本一致,没有新增的类,只是Factor增加了derivative方法

ppG39HO.png

bug分析

构建过程

在构建的过程中没有出现太多困难或者其他bug,但是在强测中出现了巨大问题

强测,互测

强测中出现了一个非常弱智的bug,我直接忘记了dx(expr)前导符号位-的情况,强测挂了三个点,但互测中意外的没怎么被发现

互测中没有发现别人的bug

复杂度分析

Cogc:认知复杂度,是由sonarQube设计的一个算法,算法将一段程序代码被理解的复杂程度,估算成一个整数——可以等同于代码的理解成本。

ev(G) 基本复杂度是用来衡量程序非结构化程度的,非结构成分降低了程序的质量,增加了代码的维护难度,使程序难于理解。因此,基本复杂度高意味着非结构化程度高,难以模块化和维护。实际上,消除了一个错误有时会引起其他的错误。

iv(G) 模块设计复杂度是用来衡量模块判定结构,即模块和其他模块的调用关系。软件模块设计复杂度高意味模块耦合度高,这将导致模块难于隔离、维护和复用。模块设计复杂度是从模块流程图中移去那些不包含调用子模块的判定和循环结构后得出的圈复杂度,因此模块设计复杂度不能大于圈复杂度,通常是远小于圈复杂度。

V(G):是用来衡量一个模块判定结构的复杂程度,数量上表现为独立路径的条数,即合理的预防错误所需测试的最少路径条数,圈复杂度大说明程序代码可能质量低且难于测试和维护,经验表明,程序的可能错误和高的圈复杂度有着很大关系。

MethodCogCev(G)iv(G)v(G)
“DefinedFunc.addArrayList(String,String)”5144
“DefinedFunc.calc(String,ArrayList)”1122
DefinedFunc.clone()0111
DefinedFunc.getHashMap()0111
“Expr.action(HashMap<Factor,BigInteger>,Num)”7166
“Expr.addHash(HashMap,HashMap)”5155
Expr.addTerm(Term)1421415
Expr.change()3133
Expr.clone()0111
Expr.deepClone()0111
“Expr.deepclone(HashMap<Factor,BigInteger>)”1122
Expr.derivative(String)1122
Expr.getFlag()0111
Expr.getHashMap()0111
“Expr.mulHash(HashMap<Factor,BigInteger>,HashMap<Factor,BigInteger>,HashMap<Factor,BigInteger>)”22177
Expr.mult(Factor)0111
“Expr.multy(HashMap<Factor,BigInteger>)”1611616
Expr.multySelf(BigInteger)2233
“Expr.setHashMap(HashMap<Factor,BigInteger>)”0111
“Input.definedFunc(String,DefinedFunc)”0111
Input.setDefinedFunc(Scanner)1122
Lexer.Lexer(String)0111
Lexer.getCurToken()0111
Lexer.getCurToken2()0111
Lexer.getExp()1122
Lexer.getNum()18588
Lexer.getPow()6335
Lexer.getTri()0111
Lexer.initial()1122
Lexer.next()4233
Main.main(String[])0111
Mixture.aBoolean(Mixture)3181214
Mixture.clone()0111
“Mixture.deepclone(HashMap<TrigonoFunc,BigInteger>)”1122
Mixture.derivative(String)2222
Mixture.derivativetri(String)7344
Mixture.getFlag()0111
Mixture.getHashMap()0111
Mixture.getPow()0111
Mixture.mult(Factor)13488
Mixture.multMix(Mixture)2111313
Mixture.setFlag(String)0111
“Mixture.setHashMap(HashMap<TrigonoFunc,BigInteger>)”0111
Mixture.setPow(Pow)0111
Num.change()0111
Num.clone()0111
Num.derivative(String)0111
Num.getFlag()0111
Num.getNum()0111
Num.mult(Factor)0111
Num.setNum(BigInteger)0111
“Output.deepclone(HashMap<TrigonoFunc,BigInteger>)”1122
Output.getBigIntegerHashMap()0111
“Output.getString(HashMap<String,BigInteger>)”7145
“Output.judge(HashMap<String,BigInteger>)”3323
“Output.mixString(Mixture,BigInteger,String)”161913
“Output.powString(Pow,BigInteger,String)”7145
Output.res(Expr)161910
“Output.simMix(Mixture,Pow)”1611010
“Output.smpify(HashMap<Factor,BigInteger>)”2041315
“Output.triString(TrigonoFunc,BigInteger,String)”7168
Parser.getExpr(Factor)3133
Parser.newParser(Lexer)0111
Parser.parserExpr()2133
Parser.parserFactor()2671414
Parser.parserFunc(String)2241111
Parser.parserTerm()2133
Parser.parserTri(String)2561112
Parser.setDefinedFunc(DefinedFunc)0111
Pow.clone()0111
Pow.compare(Pow)0111
“Pow.deepclone(HashMap<String,BigInteger>)”1122
Pow.derivative(String)8566
Pow.getFlag()0111
Pow.getHashMap()0111
Pow.getString()0111
Pow.mult(Factor)9244
Pow.setFlag(String)0111
“Pow.setHashMap(HashMap<String,BigInteger>)”0111
“Pow.setNameExp(String,BigInteger)”0111
Pretreat.pretreat(String)0111
Term.addFactor(Factor)2521011
Term.change()3133
Term.clone()0111
“Term.deepclone(HashMap<Factor,BigInteger>)”1122
Term.getHashMap()0111
“Term.getNum(HashMap<Factor,BigInteger>)”2222
“Term.isNum(HashMap<Factor,BigInteger>)”1133
Term.judge()0111
Term.setFlag(String)0111
“Term.setHashMap(HashMap<Factor,BigInteger>)”0111
TrigonoFunc.aBoolean(TrigonoFunc)1222
TrigonoFunc.change()0111
TrigonoFunc.clone()0111
“TrigonoFunc.deepclone(HashMap<TrigonoFunc,BigInteger>)”1122
TrigonoFunc.derivative(String)6144
TrigonoFunc.getExpr()0111
TrigonoFunc.getFlag()0111
TrigonoFunc.getTri()0111
TrigonoFunc.mult(Factor)9166
TrigonoFunc.setExpr(Expr)0111
TrigonoFunc.setFlag(String)0111
TrigonoFunc.setTri(String)0111
ClassOCavgOCmaxWMC
DefinedFunc2.0048
Expr3.07846
Input1.5023
Lexer2.22620
Main1.0011
Mixture3.231142
Num1.0017
Output5.801258
Parser5.751346
Pow1.73519
Pretreat1.0011
Term2.401124
TrigonoFunc1.83622

下面是一些超标的方法

pp8o6oV.png

可以注意到Mixture类判断同类项的方法,Parser的解析方法,出现复杂度较高的方法都有着大量遍历或者不断解析新字符串的特点,因为我很多地方其实都有冗余的代码,自己又懒得改,复杂度高其实也是正常,之后应该尝试降低代码复杂度

总结

从结果来看,第一次作业是不成功的,三次强测都出现了错误,但是在学习的过程中,我深刻感受到了面向对象与面向过程之间的差异:只需要让每个部分做自己的事情

除此之外,还有就是测试的重要性,第一次强测前,我只进行了小部分的手捏数据测试,自认为遍历了大部分可能,却忽略了在复杂式子时迭代器的缺陷导致强测直接暴死,第二次强测之前进行了较多次数的测试,发现了深克隆这一重要的Bug不至于暴死强测,但是因为在修改完bug之后已经深夜,就没有再测试多余的数据,强测错了一个点,第三次强测之前尝试用同学的评测机跑数据,但是始终得到的时fail的结果,我同第一次一样,仅仅手捏了几组数据就草草过了中测弱测,结果却忽略了一个基本的信息:求导因子的前导符号,强测又双叒叕挂了三个点。OO的三次作业让我深刻认识到了进行大量测试的重要性,在之后的作业里,一定要进行大量且可靠的测试

总之希望自己可以有所进步orzzzzzzzzz。

这篇关于OO_BUAA_Unit1的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/607284

相关文章

2015年多校联合训练第一场OO’s Sequence(hdu5288)

题意:给定一个长度为n的序列,规定f(l,r)是对于l,r范围内的某个数字a[i],都不能找到一个对应的j使得a[i]%a[j]=0,那么l,r内有多少个i,f(l,r)就是几。问所有f(l,r)的总和是多少。 公式中给出的区间,也就是所有存在的区间。 思路:直接枚举每一个数字,对于这个数字,如果这个数字是合法的i,那么向左能扩展的最大长度是多少,向右能扩展的最大长度是多少,那么i为合法的情况

OO原则

在看《HeadFirst》的过程中发现了OO原则和之前学过的《大话设计模式》有点不一样的的地方。总结一下,分享给大家。 首先我们来看《大话设计模式》里的六大原则。也可以访问之前的博客《23种设计模式》。 1、开放-封闭原则,是说软件实体(类、模块、函数等等)应该可以扩展,但是不可修改。 2、单一职责原则(SRP),就一个类而言,应该仅有一个引起它变化的原因。 3、依赖倒转原则:A.

嵌入式C语言OO编程方法

原文转自:http://blog.csdn.net/ice1224/article/details/4414183 嵌入式C语言OO编程方法 目前嵌入式系统的代码量也在逐渐增加,即使对于不是非常复杂的嵌入式系统,单板执行代码量在1M以下,源代码往往也会在两三万行以上。因此代码重用的要求也渐渐迫切。   本文参考OO与组件技术,提出嵌入式C语言组件编程方法,针对没有内建专业OS的

函数式编程和OO编程02——二者的等价性

我:理论上这两种方式可以等价吗?(FP 和 OOP) -ChatGPT 理论上,函数式编程(FP)和面向对象编程(OO)可以在功能上达到等价的效果,但它们的实现方式、代码结构以及思维模式有所不同。 功能等价性 从功能角度来看,两种编程范式都可以用来解决相同的问题,并实现相同的计算或逻辑。无论是用OO编程中的类和对象,还是用FP中的函数和不可变数据,都可以构建相同的应用程序或系统。 例如,

《计算机英语》Unit1 计算机概述

期末试卷组成 1、选择20道 2、判断20道 3、词汇翻译(单词+词组,参照课后习题) 4、翻译2道(一道原题,参照作业) Unit One Computer Overview 单元1 计算机概述 algorithm          n.  算法  operate          v.  操作  digital           adj. 数字的  integrated

OO U4 博客

文章目录 正向建模与开发单元架构设计与追踪关系单元架构设计追踪关系 架构设计思维进化测试思维演进课程收获 正向建模与开发 在本单元中,我学习了UML这一建模工具。UML具备相当多种类的图,通过先设计UML图再进行开发,能够避免架构的重大调整,提前察觉当前设计存在的问题。 通过状态图,可以清晰地捕捉和表达系统需求,确保我们在开发的时候充分理解需求。类图有助于系统的详细设计,提供了

BUAA-2024年春-OO第四单元总结

正向建模与开发 在本单元中,我们需要模拟一个小型的图书管理系统,完成图书馆所支持的相关业务,并遵守一定的规章制度。与前几次不同的是,本单元中,我们需要预先将自己的设计思路用UML来实现,然后进行编程。 具体而言,在本单元中,我们分别使用UML类图、状态图和顺序图来进行正向建模。类图主要展示各个类之间的关联、依赖、组合、聚合关系等;状态图则需要通过trigger、guard等来表示不同状态之间的

设置 oo alv单元格焦点

设置ooalv的单元格焦点,可能的需求情况是alv可以编辑,进入alv展示的时候,焦点是在输入tcode的地方,此时可以通过以下代码设置焦点到alv上。 DATA : IS_ROW_ID TYPE LVC_S_ROW,IS_COLUMN_ID TYPE LVC_S_COL,IS_ROW_NO TYPE LVC_S_RO

sap oo alv 得到过滤掉的数据行

在使用sap alv开发的表中中,用户有时需要通过标准的过滤按钮筛选 数据,如果此时自定义了全选和取消全选(非标准的实现)功能,那么需要获得排除的数据行。 DATA : ET_FILTERED TYPE LVC_T_FIDX.RANGES : R_INDEX FOR E_INDEX. "过滤掉的程序行"得到过滤掉的行CLEAR : ET_FILTERED.CALL ME

shell 简单oo编程

昨天突然有个疑问,linux shell能不能像power shell面向对象编程了。然后上班的时候就搜了下,还真有! 下面是拷贝过来的代码: #!/bin/bash# ---------------------------------------------------------------------------# OO support functions# Kludged by