BUAA ObjectOriented Unit1总结

​ 一句话来说,第一单元的作业就是处理一个表达式,展开其中不必要的括号并尽可能进行化简,从而输出化简后最短的字符串。其中,第一次作业仅需支持幂函数,而第二次作业需要支持简单情况下的三角函数、自定义函数以及求和函数,到了第三次作业,这些函数之间可以互相嵌套而产生递归调用。可以说,这些作业有着非常多的细节需要仔细考量,但总体来说,可以将其分解成三个互相独立的部分:

  • 解析输入
  • 数学化简
  • 输出结果

这三个模块的耦合程度非常低,可分别进行单独调试,遇到了bug也非常容易对其定位并加以解决,至于每次作业的迭代,都只需分别对这三个部分进行修改,无需进行大幅度重构。(此处省略几万字自卖自夸......)

第一次作业

代码部分

架构分析

解析输入

​ 在第一次作业中,数据结构为三个部分:

  • Expr\(\rightarrow\)Term \(|\) Expr \(\pm\) Term
  • Term \(\rightarrow\) Factor \(|\) Term \(\times\) Factor
  • Factor \(\rightarrow\) Number \(|\) Pow \(|\) Expr

通过这三个部分就可以把数据存储起来。那怎么处理带指数的Expr呢?我的做法是,当场读,当场解析,读到带指数的Expr时,我返回的是指数Factor(当然,指数为0就让整体直接返回1),这样一处理,就没有带指数的Expr了。

​ 按照上述的数据结构,使用递归下降的方法就能轻松地完成输入的解析。值得一提的是,如果想要支持Wrong Format!的输出,就要对解析到的每一个单元进行格式判断,使得代码变长,但如果预先假定所有的输入都是符合规范的,就可以省去很多不必要的麻烦。

数学化简

第一次作业的化简还是比较简单的,首先是使用Expr.addsimplify()方法遍历Expr的每一个Term,看看是否有FactorExpr的形式(也就是是否有括号),如果有,那就对该Term使用Term.multsimplify()的方法进行化简,返回一个新的(也就是拆完括号后的)Expr,而Term.multsimplify()中涉及到FactorExpr之间的相乘,普通的乘法(FactorExpr)可以直接用ArrayListadd方法进行实现,唯一要考虑的就是ExprExpr的相乘。因此需要构造Expr.mult()方法来分别遍历两个ExprTerm,通过合并Term中装载FactorArrayList进行乘法运算,得到新的(也就是乘完后的)Expr,再对该Expr进行递归的化简,之后返回化简后的(也就是无括号的)Expr。如此一来,Expr中就没有括号了,之后通过HashMap储存指数与系数,就可以实现合并同类项,根据合并完同类项的HashMap,就可以返回最终化简后的(也就是无括号,无同类项的)Expr了。

​ 值得一提的是,这种化简方法极大地模拟了人手算的过程,在每一次乘法运算拆括号后都会进行加法运算来合并同类项,极大地减少了运算过程中项的数量,就算是\((1+x)^{1000}\)也能一秒得出结果。而在使用这套方法前,我使用的是另外一套化简方法,该方法基于的是乘法分配律,当我遇到Term中含有FactorExpr的形式(也就是是含有括号),我就将该Term进行分裂,成为多个新的(也就是不含有该括号的)Term,这种做法虽然代码量极少,但无法在乘法运算拆括号的过程中加入合并同类项的加法运算,导致运算过程中项会非常多,连\((1+x)^{25}\)也会产生爆栈的Error

输出结果

​ 由于这一次作业的结构非常简单(我懒癌犯了),我就没有写递归的toString方法,而是根据生成的储存指数与系数的HashMap进行输出打印。

性能优化

  • 在输出时,系数为1或-1可以进行省略,指数为1可以进行省略
  • 指数为2,就可以把\(x\ast\ast2\)化为\(x\ast x\)
  • 如何尽量保证第一项为正呢?我的做法是,如果输出时发现该Term为负,就append到最后,如果发现该Term为正,就insert到最前面。

分析部分

类图分析

复杂度分析

Class OCavg OCmax WMC
Parser 7.571428571428571 15.0 53.0
MainClass 1.0 1.0 1.0
Lexer 2.3333333333333335 6.0 14.0
expr.Term 1.4285714285714286 4.0 10.0
expr.Pow 1.0 1.0 2.0
expr.Number 1.0 1.0 2.0
expr.Exprpow 1.0 1.0 3.0
expr.Expr 6.142857142857143 18.0 43.0
Method CogC ev(G) iv(G) v(G)
Parser.powfactor() 14.0 4.0 7.0 7.0
Parser.parseTerm() 28.0 3.0 14.0 15.0
Parser.Parser(Lexer) 0.0 1.0 1.0 1.0
Parser.parseFactor() 3.0 3.0 3.0 3.0
Parser.parseExpr() 24.0 4.0 15.0 15.0
Parser.numfactor() 4.0 4.0 4.0 4.0
Parser.exprfactor() 15.0 5.0 8.0 8.0
MainClass.main(String[]) 0.0 1.0 1.0 1.0
Lexer.peek() 0.0 1.0 1.0 1.0
Lexer.next() 6.0 2.0 5.0 7.0
Lexer.Lexer(String) 0.0 1.0 1.0 1.0
Lexer.getWhite() 3.0 1.0 3.0 4.0
Lexer.getNumber() 2.0 1.0 3.0 3.0
Lexer.getMult() 2.0 1.0 3.0 3.0
expr.Term.Term() 0.0 1.0 1.0 1.0
expr.Term.setSimplified(boolean) 0.0 1.0 1.0 1.0
expr.Term.multsimplify() 7.0 1.0 4.0 4.0
expr.Term.isSimplified() 0.0 1.0 1.0 1.0
expr.Term.getFactors() 0.0 1.0 1.0 1.0
expr.Term.addFactor(Factor) 0.0 1.0 1.0 1.0
expr.Term.addAllFactor(ArrayList) 0.0 1.0 1.0 1.0
expr.Pow.Pow(int) 0.0 1.0 1.0 1.0
expr.Pow.getExponent() 0.0 1.0 1.0 1.0
expr.Number.Number(BigInteger) 0.0 1.0 1.0 1.0
expr.Number.getNum() 0.0 1.0 1.0 1.0
expr.Exprpow.getFactor() 0.0 1.0 1.0 1.0
expr.Exprpow.getExponent() 0.0 1.0 1.0 1.0
expr.Exprpow.Exprpow(Factor, int) 0.0 1.0 1.0 1.0
expr.Expr.print(HashMap) 51.0 1.0 18.0 18.0
expr.Expr.mult(Expr) 4.0 1.0 4.0 4.0
expr.Expr.getTerms() 0.0 1.0 1.0 1.0
expr.Expr.genmap() 12.0 1.0 6.0 6.0
expr.Expr.Expr() 0.0 1.0 1.0 1.0
expr.Expr.addTerm(Term) 0.0 1.0 1.0 1.0
expr.Expr.addsimplify() 42.0 1.0 13.0 13.0

​ 从总体来说,ParserExpr的复杂度较高,其他类的复杂度很低。具体原因是因为我在这两个块中实现了一些初步化简功能,虽然复杂度变高了,维护成本大了,但我认为这种做法会让我思路更清晰。但最大的败笔就是print方法,由于该方法是我偷懒写出来的输出方法,导致模块设计复杂度和圈复杂度很高。这也导致我在之后必须舍弃这个方法,重新写toString方法。

测试部分

自测

  • 和室友合作搞了一个自动评测机(基于pythonsympy),使用相同的递归下降构造表达式,之后将输出结果和sympy展开后的结果进行字符串对比
  • 手造了几组较小的数据进行测试,比如0x1-1之类的

在自测部分中,使用自动评测机跑了非常久,便认为几乎没有bug

互测

​ 公测和互测中均未被Hack,且没有丢性能分。但是在房间中成功找出了别人的1bug,该bug是由于该同学直接使用字符串替换将\(1*x\)替换为\(x\),而没有考虑完全,比如\(81*x\)中的\(1*x\)就不应该进行这样的替换。

第二次作业

代码部分

架构分析

​ 在第二次作业中,需要额外支持sincos的三角函数,f(...)g(...)h(...)的自定义函数,sum的求和函数,同时,需要支持简单的括号嵌套(srds在第一次作业中我就已经支持括号嵌套了),同时对于新加的三个函数的数据加入了一些限制,比如Factor不能是Expr等(srds我觉得第三次作业一定会支持,就在第二次作业中也支持了)。由于我的架构较好,所以只需加一些代码,不用打补丁重构啥的。

解析输入

​ 在第二次作业中,数据结构为五个部分:

  • Expr\(\rightarrow\)Term \(|\) Expr \(\pm\) Term
  • Term \(\rightarrow\) Factor \(|\) Term \(\times\) Factor
  • Factor \(\rightarrow\) Number \(|\) Pow \(|\) Expr \(|\) Function
  • Function \(\rightarrow\) Diyfunction \(|\) Sumfunction \(|\) Trifunction
  • Totaldiyfunctions

相比于第一次作业,新增了Function接口,并使其继承Factor接口,用以储存新增的三种函数Diyfunction(自定义函数)、Sumfunction(求和函数)、Trifunction(三角函数)。同时,还新增了一个比较独立的类Totaldiyfunctions用于储存自定义函数的定义式。

​ 相比于第一次作业,由于新增了三种函数,需要对原来递归下降的解析进行修改,我的做法是,当场解析,当场代入,也就是说,在解析到三种函数时,我调用该函数的simplify方法来对函数进行化简,返回化简后的Factor,这样的话,之后就没有任何后顾之忧。当然,难点就来到了每种函数的simplify方法了,除了Trifunction只需对里面的内容进行化简外,DiyfunctionSumfunction均需要使用替换带入的方法,一种简单又省事的办法就是直接使用字符串替换(干净又卫生啊),但我觉得这样不够优雅(其实是因为我觉得需要考虑字符串有重合部分,比如sini,还要考虑代入要加括号啥的太麻烦了,怕出bug),于是,我就使用了数据结构层面的递归代入,但这样做需要有一个致命前提,就是你每想实现一次代入,就要深拷贝一份新的函数内容出来,于是我就对每个类写了一个copy方法(因为我用不来自带的clone方法啊),在这些基础上,就可以实现递归的代入,在代入完成后,化简一次,目的还是如第一次作业中所说,减少项的数量,让程序能跑更大的数据,跑得更快。

​ 值得一提的是,由于新加入了三种函数,导致原来的方法会变长,很不雅观(代码风格被扣分了),所以我删除了部分Wrong Format!的拓展。

数学化简

​ 由于函数在解析时已经进行化简了,所以相比于第一次作业来说,只需修改Term的化简和Expr的化简。

​ 既然解析时已经做了函数的化简了,那数学化简部分第二次作业比第一次作业多了啥呢?答案是Trifunction。由于它的存在,不能简单地只储存系数和指数了,所以在Term中需要新写一个merge方法,该方法基于HashMap实现,该HashMap存储的是基元和指数,分别通过getRadix()getExp()得到,所谓的基元,就是不带指数的内容,当遍历到的Factor的基元在HashMap中,就将对应的值和指数相加进行合并,如果不在HashMap中,就新建一个基元和指数的键值对。

​ 至于Expr中的合并同类项,需要修改原HashMap中的内容。由于项不再是简单的幂函数形式,项是否相等该如何判断呢?答案是重写每一个类的hashCodeequals方法。之后在Expr中建立一个HashMapHashSet的结构(即HashMap<HashSet<Factor>, BigInteger>),键是merge之后的Term,值为Coefficient(也就是系数),合并同类项的方法也和第一次作业一样,遍历到的TermHashMap中,就将系数和对应的值相加,如果该Term不在HashMap中,就新建一个键值对。

输出结果

​ 由于项变复杂了,第一次作业中的简单处理不好用了,于是我在ExprTermFactor中构造了递归的toString()方法,实现了答案的输出。

性能优化

在第一次作业的基础上,还做了以下优化:

  • Trifunction中为0时,转为Number01
  • Trifunction里面的负号提到外面
  • 由于当时时间不够(这次是真的时间不够,不是懒癌犯了),就没有做\(sin(x)^2+cos(x)^2=1\)的化简

分析部分

类图分析

复杂度分析

Class OCavg OCmax WMC
Parser 8.818181818181818 16.0 97.0
MainClass 5.0 5.0 5.0
Lexer 2.111111111111111 7.0 19.0
expr.Trifunction 1.8571428571428572 5.0 26.0
expr.Totaldiyfunctions 1.0 1.0 3.0
expr.Term 4.466666666666667 16.0 67.0
expr.Sumfunction 3.4 13.0 17.0
expr.Pow 1.2222222222222223 3.0 11.0
expr.Number 1.2857142857142858 3.0 9.0
expr.Exprpow 1.0 1.0 6.0
expr.Expr 2.4615384615384617 9.0 32.0
expr.Diyfunction 2.2857142857142856 9.0 16.0
Method CogC ev(G) iv(G) v(G)
Parser.trifactor(String) 39.0 12.0 14.0 16.0
Parser.sumfactor() 14.0 1.0 13.0 13.0
Parser.removed(String, Number, int) 2.0 2.0 2.0 2.0
Parser.powfactor(String) 14.0 4.0 7.0 7.0
Parser.parseTerm() 28.0 3.0 14.0 15.0
Parser.Parser(Lexer) 0.0 1.0 1.0 1.0
Parser.parseFactor() 9.0 6.0 12.0 12.0
Parser.parseExpr() 24.0 4.0 15.0 15.0
Parser.numfactor() 4.0 4.0 4.0 4.0
Parser.exprfactor() 15.0 5.0 8.0 8.0
Parser.diyfactor(String) 14.0 4.0 10.0 10.0
MainClass.main(String[]) 7.0 1.0 5.0 5.0
Lexer.setTotaldiyfunctions(Totaldiyfunctions) 0.0 1.0 1.0 1.0
Lexer.peek() 0.0 1.0 1.0 1.0
Lexer.next() 8.0 2.0 6.0 9.0
Lexer.Lexer(String) 0.0 1.0 1.0 1.0
Lexer.getWhite() 3.0 1.0 3.0 4.0
Lexer.getTotaldiyfunctions() 0.0 1.0 1.0 1.0
Lexer.getNumber() 2.0 1.0 3.0 3.0
Lexer.getMult() 2.0 1.0 3.0 3.0
Lexer.getFunction() 2.0 1.0 3.0 3.0
expr.Trifunction.Trifunction(String, int, Factor) 0.0 1.0 1.0 1.0
expr.Trifunction.toString() 5.0 1.0 5.0 5.0
expr.Trifunction.substitute(String, Factor) 7.0 1.0 5.0 5.0
expr.Trifunction.substitute(BigInteger) 3.0 1.0 3.0 3.0
expr.Trifunction.setExponent(int) 0.0 1.0 1.0 1.0
expr.Trifunction.setContent(Factor) 0.0 1.0 1.0 1.0
expr.Trifunction.hashCode() 0.0 1.0 1.0 1.0
expr.Trifunction.getRadix() 0.0 1.0 1.0 1.0
expr.Trifunction.getName() 0.0 1.0 1.0 1.0
expr.Trifunction.getExponent() 0.0 1.0 1.0 1.0
expr.Trifunction.getExp() 0.0 1.0 1.0 1.0
expr.Trifunction.getContent() 0.0 1.0 1.0 1.0
expr.Trifunction.equals(Object) 4.0 3.0 4.0 6.0
expr.Trifunction.copy() 0.0 1.0 1.0 1.0
expr.Totaldiyfunctions.Totaldiyfunctions() 0.0 1.0 1.0 1.0
expr.Totaldiyfunctions.getDiyfunctions() 0.0 1.0 1.0 1.0
expr.Totaldiyfunctions.addFunction(Diyfunction) 0.0 1.0 1.0 1.0
expr.Term.toString() 21.0 1.0 12.0 12.0
expr.Term.Term() 0.0 1.0 1.0 1.0
expr.Term.substitute(String, Factor) 40.0 1.0 16.0 16.0
expr.Term.substitute(BigInteger) 28.0 1.0 13.0 13.0
expr.Term.setSimplified(boolean) 0.0 1.0 1.0 1.0
expr.Term.multsimplify() 7.0 1.0 4.0 4.0
expr.Term.merge() 18.0 1.0 9.0 9.0
expr.Term.isSimplified() 0.0 1.0 1.0 1.0
expr.Term.hashCode() 0.0 1.0 1.0 1.0
expr.Term.getFactors() 0.0 1.0 1.0 1.0
expr.Term.getCoefficient() 0.0 1.0 1.0 1.0
expr.Term.equals(Object) 4.0 3.0 3.0 5.0
expr.Term.copy() 1.0 1.0 2.0 2.0
expr.Term.addFactor(Factor) 0.0 1.0 1.0 1.0
expr.Term.addAllFactor(ArrayList) 0.0 1.0 1.0 1.0
expr.Sumfunction.Sumfunction(BigInteger, BigInteger, Factor) 0.0 1.0 1.0 1.0
expr.Sumfunction.simplify() 29.0 1.0 12.0 13.0
expr.Sumfunction.getRadix() 0.0 1.0 1.0 1.0
expr.Sumfunction.getExp() 0.0 1.0 1.0 1.0
expr.Sumfunction.copy() 0.0 1.0 1.0 1.0
expr.Pow.setName(String) 0.0 1.0 1.0 1.0
expr.Pow.Pow(String, int) 0.0 1.0 1.0 1.0
expr.Pow.hashCode() 0.0 1.0 1.0 1.0
expr.Pow.getRadix() 0.0 1.0 1.0 1.0
expr.Pow.getName() 0.0 1.0 1.0 1.0
expr.Pow.getExponent() 0.0 1.0 1.0 1.0
expr.Pow.getExp() 0.0 1.0 1.0 1.0
expr.Pow.equals(Object) 4.0 3.0 3.0 5.0
expr.Pow.copy() 0.0 1.0 1.0 1.0
expr.Number.Number(BigInteger) 0.0 1.0 1.0 1.0
expr.Number.hashCode() 0.0 1.0 1.0 1.0
expr.Number.getRadix() 0.0 1.0 1.0 1.0
expr.Number.getNum() 0.0 1.0 1.0 1.0
expr.Number.getExp() 0.0 1.0 1.0 1.0
expr.Number.equals(Object) 3.0 3.0 2.0 4.0
expr.Number.copy() 0.0 1.0 1.0 1.0
expr.Exprpow.getRadix() 0.0 1.0 1.0 1.0
expr.Exprpow.getFactor() 0.0 1.0 1.0 1.0
expr.Exprpow.getExponent() 0.0 1.0 1.0 1.0
expr.Exprpow.getExp() 0.0 1.0 1.0 1.0
expr.Exprpow.Exprpow(Factor, int) 0.0 1.0 1.0 1.0
expr.Exprpow.copy() 0.0 1.0 1.0 1.0
expr.Expr.toString() 8.0 1.0 5.0 5.0
expr.Expr.substitute(String, Factor) 1.0 1.0 2.0 2.0
expr.Expr.substitute(BigInteger) 1.0 1.0 2.0 2.0
expr.Expr.mult(Expr) 4.0 1.0 4.0 4.0
expr.Expr.hashCode() 0.0 1.0 1.0 1.0
expr.Expr.getTerms() 0.0 1.0 1.0 1.0
expr.Expr.getRadix() 0.0 1.0 1.0 1.0
expr.Expr.getExp() 0.0 1.0 1.0 1.0
expr.Expr.Expr() 0.0 1.0 1.0 1.0
expr.Expr.equals(Object) 3.0 3.0 2.0 4.0
expr.Expr.copy() 1.0 1.0 2.0 2.0
expr.Expr.addTerm(Term) 0.0 1.0 1.0 1.0
expr.Expr.addsimplify() 22.0 4.0 8.0 9.0
expr.Diyfunction.simplify(Totaldiyfunctions) 24.0 3.0 9.0 9.0
expr.Diyfunction.setContent(Expr) 0.0 1.0 1.0 1.0
expr.Diyfunction.getRadix() 0.0 1.0 1.0 1.0
expr.Diyfunction.getExp() 0.0 1.0 1.0 1.0
expr.Diyfunction.Diyfunction(String) 0.0 1.0 1.0 1.0
expr.Diyfunction.copy() 1.0 1.0 2.0 2.0
expr.Diyfunction.addArgument(Factor) 0.0 1.0 1.0 1.0

​ 第二次作业复杂度较高的几个地方主要是新加入的三种函数的部分,由于要使用递归替换,而且为了性能,替换中总要特判几下(比如处理sin(0)sin(-1)之类的数据),并且,这三个函数的加入加重了Parser的复杂度,原因是我在Parser中就要将新加入的三种函数化简了,这也是架构带来的硬伤。之后就是TermtoString方法了,原因是不同因子输出的格式有所不同,且为了性能得加入很多特判。(虽然也有我代码水平的问题了)总体来说,由于第二次作业花的时间少,导致复杂度非常高,可以说是是三次作业中最丑的了。

测试部分

自测

  • 在第一作业的评测机上进行了修改,由于pythonsympy会对三角函数进行化简,所以不能使用字符串对比的方法来验证是否正确,而是改为将100个数代入后对比得到的结果是否一样。
  • 手造了几组较小的数据进行测试,比如0sin(x)1-1之类的
  • 值得一提的是,评测机生成了一个答案约为72.7k的数据,我们整个宿舍都觉得这个数据非常强,就把它奉为检验的最强防线,这个数据甚至沿用到了第三次作业

互测

​ 公测和互测中被Hack了一个点,该点的bug是由于手误,if...else...结构中少写了三行的else...内容,能暴露这个bug的只能是非常限定的小数据。这件事也告诉我,不能过度依靠评测机,评测机虽然能生成非常复杂的数据来检测,但还需自己构造那些非常限定的小数据。同时,我还在房间中成功找出了别人的1bug,该bug是由于该同学对于\(sin(-1)^2\)等数据中,将-直接提到外面,不考虑指数。除了WA掉一个点外,由于没有做\(sin(x)^2+cos(x)^2=1\)的化简,导致性能分也丢了5分。(属实是屋漏偏逢连夜雨了)

第三次作业

代码部分

架构分析

​ 相比于第二次作业,第三次作业基本上没有什么新东西的加入,我只做了一些代码的美化工作,比如把公共部分提出来成为新的方法。

解析输入

​ 数据结构还是和第二次作业一样的五个部分,解析时遇到三种函数也是当场就进行化简。

数学化简

​ 相比于第二次作业,实现了绝大多数情况下\(sin(x)^2+cos(x)^2=1\)的化简。具体方法是,遍历地将\(sin(Factor)^k\times Term(k\ge2)\)替换为\((1-cos(Factor.simplify())^2)\times sin(Factor.simplify())^{k-2}\times Term\),将\(sin(Factor)^k\times Term(k\lt2)\)替换为$ sin(Factor.simplify())^k\times Term$,如果替换并化简后的表达式长度比原来的要更短,就输出更短的那个结果。同理,还有将cos化为sin的版本,将这三个版本的结果长度进行比较,输出最短的。这种方法虽然无法覆盖所有的情况,但它比较简单,且效果显著。

​ 其次,由于本次作业中Trifunction中可以是Expr了,为了丢掉多余的括号,在Trifunction的化简部分要加入一些判断,判断Expr中是否只有一个Term,且该Term中只有一个Factor,如果是这样的话,就可以直接拆包将里面的Factor拆出来。

​ 最后,如果Trifunction中的Expr里全是负项,就可以提出一个负号。那如何得知Expr里全是负项呢?我想到了一个简单的办法就是直接判断Expr.toString()是不是负号打头。原理就是,Expr.toString()方法中,会尽可能地让正项放在前面,如果第一项都为负了,那么整个Expr中的项都是负的,也就可以把公共的负号提出来了,具体提出的方法和第二次作业一样,sin需要考虑指数,cos直接丢掉就好。当然,在对里面的ExprExpr.toString()的方法进行判断前,需要对其使用Expr.addsimplify()方法进行化简,这也就形成了一个递归的过程,使得类似于sin(sin(sin(-1)))的数据能将最里面的负号提到最外面。

输出结果

​ 本次作业的输出与第二次作业的差别不大,唯一要注意的点是Trifunction中,如果为Expr需要加括号,其他情况则不需要(这也就是上述化简中需要尽可能拆包的原因)。

性能优化

在第二次作业的基础上,还做了以下优化:

  • Trifunction里面的负号提到外面(这次需要考虑内部为Expr和嵌套的情况)
  • \(sin(x)^2+cos(x)^2=1\)的化简

分析部分

类图分析

复杂度分析

Class OCavg OCmax WMC
Parser 6.0 18.0 72.0
MainClass 7.0 7.0 7.0
Lexer 2.111111111111111 7.0 19.0
expr.Trifunction 2.6875 7.0 43.0
expr.Totaldiyfunctions 1.0 1.0 3.0
expr.Term 4.176470588235294 14.0 71.0
expr.Sumfunction 3.2 12.0 16.0
expr.Pow 1.25 3.0 10.0
expr.Number 1.2857142857142858 3.0 9.0
expr.Exprpow 1.1428571428571428 2.0 8.0
expr.Expr 3.0 12.0 45.0
expr.Diyfunction 2.2857142857142856 9.0 16.0
Method CogC ev(G) iv(G) v(G)
Parser.trifactor(String) 36.0 13.0 16.0 19.0
Parser.sumfactor() 6.0 1.0 5.0 5.0
Parser.removed(String, Number, int) 2.0 2.0 2.0 2.0
Parser.removed(String, Expr, int) 10.0 3.0 6.0 6.0
Parser.powfactor(String) 7.0 3.0 4.0 4.0
Parser.parseTerm() 18.0 1.0 10.0 10.0
Parser.Parser(Lexer) 0.0 1.0 1.0 1.0
Parser.parseFactor() 9.0 6.0 12.0 12.0
Parser.parseExpr() 10.0 4.0 8.0 8.0
Parser.numfactor() 3.0 3.0 3.0 3.0
Parser.exprfactor() 8.0 4.0 5.0 5.0
Parser.diyfactor(String) 5.0 4.0 5.0 5.0
MainClass.main(String[]) 11.0 1.0 8.0 8.0
Lexer.setTotaldiyfunctions(Totaldiyfunctions) 0.0 1.0 1.0 1.0
Lexer.peek() 0.0 1.0 1.0 1.0
Lexer.next() 8.0 2.0 6.0 9.0
Lexer.Lexer(String) 0.0 1.0 1.0 1.0
Lexer.getWhite() 3.0 1.0 3.0 4.0
Lexer.getTotaldiyfunctions() 0.0 1.0 1.0 1.0
Lexer.getNumber() 2.0 1.0 3.0 3.0
Lexer.getMult() 2.0 1.0 3.0 3.0
Lexer.getFunction() 2.0 1.0 3.0 3.0
expr.Trifunction.Trifunction(String, int, Factor) 0.0 1.0 1.0 1.0
expr.Trifunction.toString() 7.0 1.0 7.0 7.0
expr.Trifunction.substitute(String, Factor) 5.0 1.0 5.0 5.0
expr.Trifunction.substitute(BigInteger) 5.0 1.0 5.0 5.0
expr.Trifunction.sintocos() 6.0 1.0 5.0 6.0
expr.Trifunction.setContent(Factor) 0.0 1.0 1.0 1.0
expr.Trifunction.insidechange() 11.0 1.0 9.0 9.0
expr.Trifunction.hashCode() 0.0 1.0 1.0 1.0
expr.Trifunction.getRadix() 0.0 1.0 1.0 1.0
expr.Trifunction.getName() 0.0 1.0 1.0 1.0
expr.Trifunction.getExponent() 0.0 1.0 1.0 1.0
expr.Trifunction.getExp() 0.0 1.0 1.0 1.0
expr.Trifunction.getContent() 0.0 1.0 1.0 1.0
expr.Trifunction.equals(Object) 4.0 3.0 4.0 6.0
expr.Trifunction.costosin() 6.0 1.0 5.0 6.0
expr.Trifunction.copy() 0.0 1.0 1.0 1.0
expr.Totaldiyfunctions.Totaldiyfunctions() 0.0 1.0 1.0 1.0
expr.Totaldiyfunctions.getDiyfunctions() 0.0 1.0 1.0 1.0
expr.Totaldiyfunctions.addFunction(Diyfunction) 0.0 1.0 1.0 1.0
expr.Term.toString() 21.0 1.0 12.0 12.0
expr.Term.Term() 0.0 1.0 1.0 1.0
expr.Term.substitute(String, Factor) 36.0 1.0 13.0 14.0
expr.Term.substitute(BigInteger) 27.0 1.0 12.0 12.0
expr.Term.setSimplified(boolean) 0.0 1.0 1.0 1.0
expr.Term.removed(String, Number, int) 2.0 2.0 2.0 2.0
expr.Term.removed(String, Expr, int) 10.0 3.0 6.0 6.0
expr.Term.multsimplify() 7.0 1.0 4.0 4.0
expr.Term.merge() 18.0 1.0 9.0 9.0
expr.Term.isSimplified() 0.0 1.0 1.0 1.0
expr.Term.hashCode() 0.0 1.0 1.0 1.0
expr.Term.getFactors() 0.0 1.0 1.0 1.0
expr.Term.getCoefficient() 0.0 1.0 1.0 1.0
expr.Term.equals(Object) 4.0 3.0 3.0 5.0
expr.Term.copy() 1.0 1.0 2.0 2.0
expr.Term.addFactor(Factor) 0.0 1.0 1.0 1.0
expr.Term.addAllFactor(ArrayList) 0.0 1.0 1.0 1.0
expr.Sumfunction.Sumfunction(BigInteger, BigInteger, Factor) 0.0 1.0 1.0 1.0
expr.Sumfunction.simplify() 28.0 1.0 11.0 12.0
expr.Sumfunction.getRadix() 0.0 1.0 1.0 1.0
expr.Sumfunction.getExp() 0.0 1.0 1.0 1.0
expr.Sumfunction.copy() 0.0 1.0 1.0 1.0
expr.Pow.Pow(String, int) 0.0 1.0 1.0 1.0
expr.Pow.hashCode() 0.0 1.0 1.0 1.0
expr.Pow.getRadix() 0.0 1.0 1.0 1.0
expr.Pow.getName() 0.0 1.0 1.0 1.0
expr.Pow.getExponent() 0.0 1.0 1.0 1.0
expr.Pow.getExp() 0.0 1.0 1.0 1.0
expr.Pow.equals(Object) 4.0 3.0 3.0 5.0
expr.Pow.copy() 0.0 1.0 1.0 1.0
expr.Number.Number(BigInteger) 0.0 1.0 1.0 1.0
expr.Number.hashCode() 0.0 1.0 1.0 1.0
expr.Number.getRadix() 0.0 1.0 1.0 1.0
expr.Number.getNum() 0.0 1.0 1.0 1.0
expr.Number.getExp() 0.0 1.0 1.0 1.0
expr.Number.equals(Object) 3.0 3.0 2.0 4.0
expr.Number.copy() 0.0 1.0 1.0 1.0
expr.Exprpow.getRadix() 0.0 1.0 1.0 1.0
expr.Exprpow.getFactor() 0.0 1.0 1.0 1.0
expr.Exprpow.getExponent() 0.0 1.0 1.0 1.0
expr.Exprpow.getExp() 0.0 1.0 1.0 1.0
expr.Exprpow.Exprpow(Factor, int) 0.0 1.0 1.0 1.0
expr.Exprpow.expand() 1.0 1.0 2.0 2.0
expr.Exprpow.copy() 0.0 1.0 1.0 1.0
expr.Expr.toString() 8.0 1.0 5.0 5.0
expr.Expr.substitute(String, Factor) 1.0 1.0 2.0 2.0
expr.Expr.substitute(BigInteger) 1.0 1.0 2.0 2.0
expr.Expr.sintocos() 8.0 1.0 7.0 7.0
expr.Expr.mult(Expr) 4.0 1.0 4.0 4.0
expr.Expr.hashCode() 0.0 1.0 1.0 1.0
expr.Expr.getTerms() 0.0 1.0 1.0 1.0
expr.Expr.getRadix() 0.0 1.0 1.0 1.0
expr.Expr.getExp() 0.0 1.0 1.0 1.0
expr.Expr.Expr() 0.0 1.0 1.0 1.0
expr.Expr.equals(Object) 3.0 3.0 2.0 4.0
expr.Expr.costosin() 8.0 1.0 7.0 7.0
expr.Expr.copy() 1.0 1.0 2.0 2.0
expr.Expr.addTerm(Term) 0.0 1.0 1.0 1.0
expr.Expr.addsimplify() 29.0 4.0 11.0 12.0
expr.Diyfunction.simplify(Totaldiyfunctions) 24.0 3.0 9.0 9.0
expr.Diyfunction.setContent(Expr) 0.0 1.0 1.0 1.0
expr.Diyfunction.getRadix() 0.0 1.0 1.0 1.0
expr.Diyfunction.getExp() 0.0 1.0 1.0 1.0
expr.Diyfunction.Diyfunction(String) 0.0 1.0 1.0 1.0
expr.Diyfunction.copy() 1.0 1.0 2.0 2.0
expr.Diyfunction.addArgument(Factor) 0.0 1.0 1.0 1.0

​ 虽然第三次作业的指标相较于第二次作业来说并没有下降,但是也没有出现新的红指标了。这样看来的话,新加入部分的耦合度还是比较好的。

测试部分

自测

​ 和第二次作业的部分几乎一样。但有了第二次作业的教训,对小数据的构造更上心了。

互测

​ 公测和互测中均未被Hack,但是由于未做二倍角化简等丢了3分性能分。但是在房间中成功找出了别人的9bug(应该有7个不同质的)。这些bug分为这几种:

  • sum函数中,循环变量没有使用BigInteger
  • sum函数中,使用字符串替换时,没有套括号,导致\(sum(i,-2,-1,i^2)\)算出来的结果为-5
  • 对于\(sin(-1)^2\)等数据中,将-直接提到外面,不考虑指数
  • 结果为0时,输出空串
  • 结果为\(sin((-x))\)等数据中,输出的结果为\(sin(-x)\),没有输出必要的括号
  • \(sin(0)^0\)输出0而不是1

心得体会

​ 总的来说,第一单元让我懂得了很多。首先,它让我知道了我的思考总是不全面的,总是简单想完就开写,每次都会遇到致命缺陷的地方。第一次作业中,我一开始的思路的实现需要一边遍历ArrayList一边对其进行修改,这样就会造成遍历顺序出错,其次,由于使用分配律,导致算不了很复杂的式子,只能通过重构来解决,第二次作业中,由于需要使用递归替换,就必须实现拷贝的方法,而当时我并不会clone,只能写了个拷贝的方法加进去才能解决,第三次作业中,由于架构原因,每到关键步都得化简,使得代码重复部分较多,这些都是非常大的硬伤,而每次都要等到写到那个地方才能发现,且这些都不好解决,硬要解决还得从老远之前重写。其次,第一单元暴露了我两个缺点。首先是为了图方便而无所不用其极,导致代码破破烂烂,其次是为了优化性能而无所不用其极,导致代码处处臃肿,甚至还有专门外挂的方法来对输出进行优化。

​ 但不得不说,我确实感觉自己提高了很多。首先是有了一次写大工程代码的经验,让我有机会去思考如何构造大工程的代码,并合理地布局,让代码看起来思路清晰。其次,由于三次作业是递进关系,也让我收获了如何让自己的代码可迭代性更强的技巧。在知识层面上,我收获了面向对象编程的技巧,以及一些类似于递归下降,重写hashCode的方法之类的技能和技巧。同时,我也收获了成功的喜悦,在做第一次作业之前,我从来没有做大工程的经历,当看到题目后我甚至一点思路都没有,看完训练栏目后,都处于懵逼的状态,后来还是艰难地开了头,之后越写思路越清晰,到最后发现自己成功完成后还觉得很不可思议,“嘿,我还真就做出来了!”那次让我整整开心了三天。但是之后第二次作业由于时间太紧了,加上WA了一个点,心里甚至都有点不好受。第三次作业,由于加的东西太少了,没啥感觉。同时,我心境上也更加成熟了,知道万事开头难,坚持下去就一定能做出来,知道了花的时间越多,回报就越大,知道了有些东西不能去盲目追求了。

最后,虽然第二次作业时由于花费时间较少而WA了一个点,稍微有点遗憾之外,第一单元还是结束了,希望我第二单元能够做得更好吧。

posted @ 2022-03-26 13:18  praynext  阅读(35)  评论(1编辑  收藏  举报