# OO第一单元总结

## 1、度量分析

### 1.1第一次作业

#### 1.1.1类复杂度分析

• OCavg：Average Operation Complexity

• OCmax：Maximum Operation Complexity

• WMC：Weighted Method Complexity

classOCavgOCmaxWMC
Poly 5.428571428571429 10.0 38.0
Main 5.0 5.0 5.0
Parser 3.5 5.0 14.0
Lexer 2.5 6.0 10.0
Term 2.0 5.0 8.0
Expr 1.75 4.0 7.0
Number 1.0 1.0 3.0
P 1.0 1.0 4.0
Variable 1.0 1.0 2.0
Total     91.0
Average 2.757575757575758 4.222222222222222 10.11111111111111

#### 1.1.2方法复杂度分析

• CogC：Cognitive Complexity

• ev(G)：Essential cyclomatic

• iv(G)：Design Complexity

• v(G)：Cyclomaitc Complexity

methodCogCev(G)iv(G)v(G)
Poly.print() 27.0 2.0 9.0 10.0
Poly.sub(Poly) 12.0 1.0 8.0 10.0
Poly.mul(Poly) 11.0 1.0 7.0 9.0
Term.transfer() 9.0 1.0 5.0 5.0
Main.main(String[]) 7.0 3.0 6.0 6.0
Lexer.next() 6.0 2.0 6.0 8.0
Parser.parseExpr() 6.0 1.0 5.0 5.0
Parser.parseTerm() 6.0 1.0 5.0 5.0
Parser.parseFactor() 5.0 5.0 5.0 5.0
Expr.transfer() 4.0 1.0 4.0 4.0
Lexer.getNumber() 2.0 1.0 3.0 3.0
Poly.pow(int) 1.0 1.0 2.0 2.0
Expr.Expr() 0.0 1.0 1.0 1.0
Lexer.Lexer(String) 0.0 1.0 1.0 1.0
Lexer.peek() 0.0 1.0 1.0 1.0
Number.Number(BigInteger) 0.0 1.0 1.0 1.0
Number.getNumber() 0.0 1.0 1.0 1.0
Number.transfer() 0.0 1.0 1.0 1.0
P.P(BigInteger, int) 0.0 1.0 1.0 1.0
P.getC() 0.0 1.0 1.0 1.0
P.getE() 0.0 1.0 1.0 1.0
P.mul(P) 0.0 1.0 1.0 1.0
Parser.Parser(Lexer) 0.0 1.0 1.0 1.0
Poly.Poly() 0.0 1.0 1.0 1.0
Poly.getPs() 0.0 1.0 1.0 1.0
Term.Term() 0.0 1.0 1.0 1.0
Variable.Variable() 0.0 1.0 1.0 1.0
Variable.transfer() 0.0 1.0 1.0 1.0
Total 108.0 41.0 93.0 102.0
Average 3.272727272727273 1.2424242424242424 2.8181818181818183 3.090909090909091

### 1.2第二次作业

#### 1.2.1类复杂度分析

classOCavgOCmaxWMC
Unity 4.75 14.0 57.0
Processor 4.6 8.0 23.0
Parser 4.0 7.0 16.0
Lexer 3.5 10.0 14.0
Poly 2.25 6.0 27.0
Term 2.25 6.0 9.0
Main 2.0 2.0 2.0
Expr 1.75 4.0 7.0
Cos 1.6 3.0 8.0
Sin 1.6 3.0 8.0
Power 1.2 2.0 6.0
Number 1.0 1.0 3.0
Variable 1.0 1.0 2.0
Total     182.0
Average 2.757575757575758 5.153846153846154 14.0

#### 1.2.2方法复杂度分析

methodCogCev(G)iv(G)v(G)
Unity.equals(Unity) 45.0 14.0 12.0 18.0
Unity.print() 33.0 3.0 15.0 15.0
Unity.printCoss(int) 21.0 1.0 8.0 8.0
Unity.mul(Unity) 18.0 7.0 11.0 11.0
Processor.replaceSum() 17.0 1.0 7.0 8.0
Term.transfer() 14.0 1.0 7.0 7.0
Poly.print() 13.0 2.0 5.0 6.0
Lexer.next() 12.0 2.0 11.0 12.0
Processor.replaceSelf(char) 11.0 1.0 6.0 7.0
Parser.parseFactor() 7.0 7.0 7.0 7.0
Parser.parseExpr() 6.0 1.0 5.0 5.0
Parser.parseTerm() 6.0 1.0 5.0 5.0
Cos.Cos(String) 4.0 1.0 3.0 3.0
Expr.transfer() 4.0 1.0 4.0 4.0
Processor.removeBlank(String) 4.0 1.0 4.0 4.0
Sin.Sin(String) 4.0 1.0 3.0 3.0
Poly.move() 3.0 3.0 3.0 3.0
Poly.mul(Poly) 3.0 1.0 3.0 3.0
Power.equals(Power) 3.0 2.0 2.0 3.0
Processor.Processor(String, HashMap) 3.0 1.0 4.0 4.0
Cos.equals(Cos) 2.0 2.0 1.0 2.0
Lexer.getNumber() 2.0 1.0 3.0 3.0
Sin.equals(Sin) 2.0 2.0 1.0 2.0
Main.main(String[]) 1.0 1.0 2.0 2.0
Poly.negate() 1.0 1.0 2.0 2.0
Poly.pow(int) 1.0 1.0 2.0 2.0
Cos.getCo() 0.0 1.0 1.0 1.0
Cos.getExp() 0.0 1.0 1.0 1.0
Cos.transfer() 0.0 1.0 1.0 1.0
Expr.Expr() 0.0 1.0 1.0 1.0
Lexer.Lexer(String) 0.0 1.0 1.0 1.0
Lexer.peek() 0.0 1.0 1.0 1.0
Number.Number(BigInteger) 0.0 1.0 1.0 1.0
Number.getNumber() 0.0 1.0 1.0 1.0
Number.transfer() 0.0 1.0 1.0 1.0
Parser.Parser(Lexer) 0.0 1.0 1.0 1.0
Poly.Poly() 0.0 1.0 1.0 1.0
Poly.clearUnities() 0.0 1.0 1.0 1.0
Poly.getUnities() 0.0 1.0 1.0 1.0
Poly.setUnities(ArrayList) 0.0 1.0 1.0 1.0
Poly.sub(Poly) 0.0 1.0 1.0 1.0
Power.Power(BigInteger, int) 0.0 1.0 1.0 1.0
Power.getCo() 0.0 1.0 1.0 1.0
Power.getExp() 0.0 1.0 1.0 1.0
Power.mul(Power) 0.0 1.0 1.0 1.0
Processor.toString() 0.0 1.0 1.0 1.0
Sin.getCo() 0.0 1.0 1.0 1.0
Sin.getExp() 0.0 1.0 1.0 1.0
Sin.transfer() 0.0 1.0 1.0 1.0
Term.Term() 0.0 1.0 1.0 1.0
Unity.Unity(Power, HashMap, HashMap) 0.0 1.0 1.0 1.0
Unity.getCo() 0.0 1.0 1.0 1.0
Unity.getCoss() 0.0 1.0 1.0 1.0
Unity.getExp() 0.0 1.0 1.0 1.0
Unity.getPower() 0.0 1.0 1.0 1.0
Unity.getSins() 0.0 1.0 1.0 1.0
Unity.negate() 0.0 1.0 1.0 1.0
Variable.Variable() 0.0 1.0 1.0 1.0
Variable.transfer() 0.0 1.0 1.0 1.0
Total 251.0 105.0 182.0 195.0
Average 3.803030303030303 1.5909090909090908 2.757575757575758 2.9545454545454546

### 1.3第三次作业

#### 1.3.1类复杂度分析

Parser4.211.021.0
Unity 4.153846153846154 14.0 54.0
Self 4.0 11.0 32.0
Lexer 3.5 10.0 14.0
Poly 3.0 8.0 51.0
Main 2.5 3.0 5.0
Term 2.2 6.0 11.0
Expr 1.8 4.0 9.0
Sum 1.5 3.0 6.0
Variable 1.3333333333333333 2.0 4.0
Cos 1.125 2.0 9.0
Sin 1.125 2.0 9.0
F 1.0 1.0 1.0
G 1.0 1.0 1.0
H 1.0 1.0 1.0
Number 1.0 1.0 4.0
Power 1.0 1.0 4.0
Total     236.0
Average 2.5376344086021505 4.764705882352941 13.882352941176471

#### 1.3.3UML类图

https://www.cnblogs.com/hyguo-blog/p/16043795.html

## 2、bug与hack分析与反思

### 2.1自身bug分析

1. 强测做的不到位

1. 仅依赖强测，未考虑其他情况

### 2.2hack策略分析

• 做优化时要考虑算法的时间复杂度。有同学为了追求最大限度的优化，采用了贪心比较的优先队列，即：对于二倍角、平方和、提取负号等各种优化都进行一次，然后将结果进行比较最后再输出。这样虽能带来优化的最大化，但必然会导致时间复杂度增加。合理的方法是使用熔断机制等等

• 对题意的解读。题目中说常数是”有符号允许有前导零十进制数”，但未明确数据规模，许多同学其他因子处理时使用了BigInteger，但求和函数因子却忽略这一点导致出错。

• 字符串替换以及优化时的多种情况。最常见的便是负号处理，直接替换可能带来符号错误。此外，提取三角因子内部符号的优化可能也会导致符号错误。

## 3、迭代开发与架构设计体验

### 3.1HW1

• 递归下降：解析时，作者借鉴了与教程相同的方式，采用表达式-项-因子三级结构，使用了lexer词法转换器分析文法，parser解析器利用lexer解析整个表达式并将各项、因子解析存储，这是三次作业解析部分的基础

• 多项式统一处理：在解析后，如何计算的问题卡了我很久，因为各种情况实在太多。对于多种多样的情况，最好的方法便是略过他们的差异性，进行统一处理。

就像把常数、变量、表达式统一为“factor”一样，对于表达式、项、因子三级结构我均转化为多项式统一处理运算，并在多项式类中引入加减乘以及乘方三级运算。这构成了三次作业运算的基础。

### 3.2HW2

• 字符串替换：对于自定义函数与求和函数，由于第二次作业不需要递归调用，我采用了字符串替换的方法，把两者直接替换为相应的表达式字符串后再交给parser进行解析

• unity统一体架构：第二次作业最终的形式中，最终输出结果的每一项都是这样的：系数-指数-sin组-cos组，为了统一处理，我引入了unity类，其包含系数、x的指数，以及sin与cos的hashmap。

为什么是hashmap呢？因每个三角因子都有耦合的三角函数本身和其指数，所以必须采用以三角类为key的hashmap才能统一处理。相应的，多项式类内部的动态数组也从power幂函数变成了unity统一体

• 运算：乘法可以直接分块进行，对幂函数、sin与cos部分分别进行乘法，通过重写并调用幂函数的equals方法判断，若角度相同就指数相加；而加减法时同样需要判断三角函数的hashmap是否全相等。由此看出，equals方法同样需要递归调用

### 3.3HW3

• 自定义函数的处理：向内部传递形参实参一一对应的hashmap，开辟了一个新方法用于形式参数的替换，但是用一个表达式树来替换因子，而非字符串间的替换，这样处理抽象化程度更高，也不必再拘泥于字符串具体的种种限制

由于调用是递归的，那么替换以及计算也是递归的。为了处理这种递归式的替换，笔者将形参中的x全部替换为w，这样x就变为一个纯粹的实参。在变量类当中，如果判断是w、y、z便会继续替换，而x代表递归的终点，这样实现较为直观

• 三角函数内部处理：因三角函数内部也可能嵌套自定义、求和与三角因子，故三角函数的角度也从幂函数变成了多项式——为什么不是表达式呢？

如前文所提，笔者采用了两套数据结构。表达式是解析时的结构，而多项式才是最终计算结果时的结构。为了便于运算与化简，冗长的表达式必须经计算化简为多项式后才可以进行处理

这样实现意味着计算也需要递归：表达式中包含三角因子，三角因子内部又包含表达式以及多项式，通过递归的加减乘运算实现化简

• 输出的处理：鉴于三角因子与表达式的相互包含与调用，输出也需要递归的方法，先输出“sin(”，再调用多项式的输出方法。为了避免不必要的括号，又单独引入前文所述方法判断（导致了sin(-x)的bug）

• 直接调用之前的结构，如解析时直接解析w、y、z，将其归为变量类；又如自定义函数的实参代入，因实参可能是表达式，故将其统一处理为表达式因子，再利用与解析方法相似的表达式-项-因子三级结构代入替换。这样避免了大规模重构，但缺点就在于模块间耦合性增强

## 4、心得体会

• 代码风格：课程组最初引入checkstyle量化评判代码风格的方法让笔者觉得非常麻烦。然而当笔者后来进行其他编程因不检查代码风格而感到难受时，良好代码风格的必要性立刻显现出来了：

有英文意义的驼峰命名让变量的作用变得通俗易懂，无需费力猜测；必要的空格和空行的引入让代码阅读起来更加整齐和清爽；避免单独过长的方法让程序变得更加易于维护......对于工程级别的多人协作开发，只有良好的架构才能保证团队合作的效率

• Java编程思想：Java有继承、多态、封装三大思想。这次作业中，引入“factor”这一接口，让所有的因子继承它就体现了继承多态的优势：我们在因子层面不关心它到底是哪一种以及具体实现，而仅是把它抽象为项的一个组分，最终的实现放在各个类中。这样可以使层次分类更加清晰

此外，模块间的协作与调用也很好体现了面向对象的优势。如果利用面向过程来实现本次作业，则需要考虑到过多的细节，调用过多的函数。而采用面向对象的方法时，我们仅需要把整个过程抽象为解析、计算、输出这几个大的部分，具体实现交给下面的类。这样可以很方便的完成较大的任务

• 模块化与层次化：模块化层次化封装的思想与计组有异曲同工之妙，把功能与属性高度相关耦合的一系列变量组成为一个类，具体处理只在内部的方法实现，对外仅保留一个接口，这样可以有效实现“高内聚、低耦合”的设计思想。从函数到CPU部件再到对象，可以说封装贯穿于编程乃至整个计算机科学的学习

就像助教学长们针对优化时提到的，良好的架构应该是正确性与优化分开，以实现低耦合性，方便调试，这也是模块化优势的体现

• 构思与设计并行：对于一个大的工程，应该首先对之有一个整体的构思后再进行，但想一蹴而就明白全部细节后再去按图索骥是不可能的。只有先实现部分构思，才能在写代码的过程中加深对整个过程的理解，再进一步去构思与实现。换言之，这是一个两条腿走路的过程，只迈左脚和只迈右脚都是行不通的

## 附：关于hack的一个有趣的研究

• 输入表达式的有效长度至多为 50 个字符。其中输入表达式的有效长度指的是输入表达式去除掉所有空白符后剩余的字符总数。（本条与公测部分的限制不同

• 除此之外，为了限制不合理的 hack，我们要求输入表达式的代价：Cost(Expr) <= 100000

，其中表达式代价的计算方法如下

• Cost(常数) = max(1, len(常数))

• Cost(x) = 1

• Cost(a ** b)= Cost(a)^max(b, 1) * max(b, 1) + 2

• Cost(+a) = Cost(-a) = Cost(a) + 1

• Cost(sum(i, s, e, t)) = max(Cost(s), Cost(e)) * max(1, e - s + 1) * Cost(t)

• 满足上述要求后，我们会用官方的基准思路程序对输入数据进行检验，输出有效长度不超过 1000 的视为合法互测数据。（本条与公测部分的限制不同

posted on 2022-03-26 12:59  继续宠爱  阅读(67)  评论(1编辑  收藏  举报