BUAA-OO Unit1 总结
Posted wnico
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BUAA-OO Unit1 总结相关的知识,希望对你有一定的参考价值。
buaa oo unit1 第一单元
总述
本博客为2023年北京航空航天大学面向对象课程第一单元作业的总结,作业要求如下:
- 简单表达式的化简
- 包含函数调用与有嵌套的三角函数的表达式化简
- 包含函数调用与有嵌套的三角函数与偏导数的表达式化简
总之就是越来越变态
架构设计体验
主要架构
使用的架构将表达式解析、运算与表达式存储相分离:
-
表达式解析通过 Lexer 部分实现,将表达式转为 Token 列表
-
Parser 部分对于解析后的 Token 列表进行分析,并建构表达式树
-
表达式存储部分由 Monomial 类(hw1中为 Polynomial)构成,可以通过add,multiply 方法自动建构表达式树,实现表达式逻辑
在 hw2 以后的架构中,存在 MonoSum 和 MonoProd 两个主要的表达式类,基类 Monomial 包含该式是否被三角函数包括的信息。因此最简化下,表达式树将维护这样的一个性质:(需要注意的是,以下表述中的表达式、项、因子等称呼与课程组设定的题设没有关系)
- MonoProd 类所链接的表达式只能是被三角函数包含的 Monomial
- MonoSum 类所链接的表达式只能是被三角函数包含的 Monomial 或者 MonoProd
也就是说一个表达式集合的类只能包含不可分的因子或优先级更高的表达式集合,在维护这一关系的情况下,我们可以认为此时表达式是最简化的(不包含其他优化方案)
由于代码编写的方便(可能也没有特别方便),原本应该分离进 Variable、Constant 类的 x,y,z,1,0 等仍通过已有的两个子类进行表示。可以认为按照上述的设计思路,事实可以实现一个满足任意线性运算(?)和任意变量的代数式存储器
迭代
由于架构分离的形式,我在 hw1 向 hw2 的迭代中主要修改了表达式存储相关的内容,完成了 hw2 以后的架构设计,而表达式树内部的结构并不在解析与运算相关部分的考虑之中。
代入
对于 hw2 中重要功能自定义函数,我的处理方法是在表达式类中添加 apply 方法,因而可以将表达式中的任意变量替换为一个新的表达式并递归进行运算。具体对于变量的读取交由 Parser 部分来处理。实际上,apply 方法是对于任意未知数代入行为的抽象,因而不仅可以用于实现自定义函数的功能。
求导
hw3 中重要的求导功能是通过添加 derivative 方法实现的。由于原本代码中对于表达式类存在一定的调用关系,因而对于过程较为复杂的求导中的反复调用,可能会出现修改到其他数据的情况。
对于此问题,一个较为经典且暴力的解法是深克隆,在每次需要调用时重新克隆一次自身,以避免发生问题
另外一种想法是,一旦一个新的类被创建,它便不可再修改:用类似于 BigInteger 的设计,从而避免暴力递归深克隆带来的运行时间问题。我在代码中部分实现了这一想法,由于屎山一些问题,部分地方依然使用了暴力深克隆的方式。
类图
别骂自动生成,我有很努力的让三张图看起来有迭代关系了QAQ
上面讲的应该比较清楚了所以这里只放图。
hw1:
hw2:
hw3:
基于度量的分析
不要看了就是一坨
因为过度集中的设计导致 MonoProd 和 MonoSum 类都具有超出预期的复杂度
下次还是不脱离面向对象思想在数据结构上搞 trick 了。。。
应该会尽可能的贴近课程组理想的架构模式
Lexer.Lexer(String) | 1.0 | 1.0 | 1.0 | 2.0 |
---|---|---|---|---|
Lexer.mergeAddSub(Token, Token) | 7.0 | 4.0 | 6.0 | 7.0 |
Lexer.tokenClear(ArrayList) | 38.0 | 8.0 | 24.0 | 26.0 |
Lexer.tokenize() | 9.0 | 3.0 | 8.0 | 9.0 |
Main.main(String[]) | 10.0 | 4.0 | 7.0 | 7.0 |
Monomial.add(Monomial) | 0.0 | 1.0 | 1.0 | 1.0 |
Monomial.addFront(String) | 2.0 | 2.0 | 2.0 | 2.0 |
Monomial.apply(HashMap) | 0.0 | 1.0 | 1.0 | 1.0 |
Monomial.clone() | 0.0 | 1.0 | 1.0 | 1.0 |
Monomial.deriviate(String) | 0.0 | 1.0 | 1.0 | 1.0 |
Monomial.fastPow(int, int, int) | 3.0 | 1.0 | 1.0 | 3.0 |
Monomial.getFront() | 0.0 | 1.0 | 1.0 | 1.0 |
Monomial.isSplittable() | 0.0 | 1.0 | 1.0 | 1.0 |
Monomial.Monomial() | 0.0 | 1.0 | 1.0 | 1.0 |
Monomial.Monomial(String) | 0.0 | 1.0 | 1.0 | 1.0 |
Monomial.multiply(Monomial) | 0.0 | 1.0 | 1.0 | 1.0 |
Monomial.negate() | 0.0 | 1.0 | 1.0 | 1.0 |
Monomial.parseToken(Token) | 8.0 | 3.0 | 6.0 | 6.0 |
Monomial.power(BigInteger) | 0.0 | 1.0 | 1.0 | 1.0 |
Monomial.removeZero(HashMap) | 7.0 | 1.0 | 4.0 | 4.0 |
Monomial.setFront(String) | 0.0 | 1.0 | 1.0 | 1.0 |
Monomial.simplify() | 0.0 | 1.0 | 1.0 | 1.0 |
MonoProd.add(Monomial) | 0.0 | 1.0 | 1.0 | 1.0 |
MonoProd.apply(HashMap) | 4.0 | 1.0 | 5.0 | 5.0 |
MonoProd.buildProd(Monomial) | 3.0 | 3.0 | 3.0 | 3.0 |
MonoProd.buildProd(MonoProd) | 11.0 | 1.0 | 5.0 | 5.0 |
MonoProd.buildProd(MonoSum) | 3.0 | 2.0 | 2.0 | 3.0 |
MonoProd.clone() | 1.0 | 1.0 | 2.0 | 2.0 |
MonoProd.deriviate(String) | 28.0 | 9.0 | 10.0 | 10.0 |
MonoProd.deriviateTriple(String) | 2.0 | 1.0 | 3.0 | 3.0 |
MonoProd.equals(Object) | 18.0 | 7.0 | 10.0 | 11.0 |
MonoProd.getExpX() | 0.0 | 1.0 | 1.0 | 1.0 |
MonoProd.getExpY() | 0.0 | 1.0 | 1.0 | 1.0 |
MonoProd.getExpZ() | 0.0 | 1.0 | 1.0 | 1.0 |
MonoProd.hashCode() | 3.0 | 1.0 | 3.0 | 4.0 |
MonoProd.MonoProd() | 0.0 | 1.0 | 1.0 | 1.0 |
MonoProd.MonoProd(Monomial, BigInteger) | 13.0 | 2.0 | 6.0 | 7.0 |
MonoProd.MonoProd(Monomial, Monomial) | 1.0 | 1.0 | 2.0 | 2.0 |
MonoProd.multiply(Monomial) | 6.0 | 4.0 | 4.0 | 4.0 |
MonoProd.negate() | 1.0 | 1.0 | 2.0 | 2.0 |
MonoProd.power(BigInteger) | 4.0 | 2.0 | 3.0 | 3.0 |
MonoProd.setExpX(BigInteger) | 0.0 | 1.0 | 1.0 | 1.0 |
MonoProd.setExpY(BigInteger) | 0.0 | 1.0 | 1.0 | 1.0 |
MonoProd.setExpZ(BigInteger) | 0.0 | 1.0 | 1.0 | 1.0 |
MonoProd.simplify() | 6.0 | 2.0 | 4.0 | 7.0 |
MonoProd.toString() | 21.0 | 1.0 | 14.0 | 14.0 |
MonoSet.MonoSet() | 0.0 | 1.0 | 1.0 | 1.0 |
MonoSum.add(Monomial) | 0.0 | 1.0 | 1.0 | 1.0 |
MonoSum.apply(HashMap) | 1.0 | 1.0 | 2.0 | 2.0 |
MonoSum.buildSum(Monomial) | 3.0 | 1.0 | 3.0 | 3.0 |
MonoSum.buildSum(MonoProd) | 2.0 | 1.0 | 2.0 | 2.0 |
MonoSum.buildSum(MonoSum) | 11.0 | 1.0 | 5.0 | 5.0 |
MonoSum.clone() | 1.0 | 1.0 | 2.0 | 2.0 |
MonoSum.deriviate(String) | 7.0 | 2.0 | 5.0 | 5.0 |
MonoSum.equals(Object) | 20.0 | 8.0 | 10.0 | 12.0 |
MonoSum.hashCode() | 3.0 | 1.0 | 3.0 | 4.0 |
MonoSum.MonoSum() | 0.0 | 1.0 | 1.0 | 1.0 |
MonoSum.MonoSum(BigInteger, Monomial) | 5.0 | 1.0 | 5.0 | 5.0 |
MonoSum.MonoSum(Monomial, Monomial) | 4.0 | 1.0 | 3.0 | 3.0 |
MonoSum.multiply(Monomial) | 15.0 | 3.0 | 8.0 | 8.0 |
MonoSum.negate() | 4.0 | 2.0 | 3.0 | 3.0 |
MonoSum.power(BigInteger) | 13.0 | 4.0 | 6.0 | 6.0 |
MonoSum.simplify() | 9.0 | 5.0 | 7.0 | 8.0 |
MonoSum.toString() | 27.0 | 3.0 | 11.0 | 12.0 |
Parser.getResult() | 0.0 | 1.0 | 1.0 | 1.0 |
Parser.parse(ArrayList) | 26.0 | 4.0 | 10.0 | 16.0 |
Parser.parseArgs(ArrayList, String) | 18.0 | 1.0 | 7.0 | 9.0 |
Parser.Parser(ArrayList) | 1.0 | 1.0 | 1.0 | 2.0 |
Parser.Parser(ArrayList, HashMap, HashMap>) | 1.0 | 1.0 | 1.0 | 2.0 |
Parser.recurse(ArrayList, int) | 15.0 | 12.0 | 9.0 | 12.0 |
Token.extendAddSub(Token) | 5.0 | 1.0 | 1.0 | 5.0 |
Token.extendValue(String) | 0.0 | 1.0 | 1.0 | 1.0 |
Token.getType() | 0.0 | 1.0 | 1.0 | 1.0 |
Token.getValue() | 0.0 | 1.0 | 1.0 | 1.0 |
Token.setType(String) | 0.0 | 1.0 | 1.0 | 1.0 |
Token.Token() | 0.0 | 1.0 | 1.0 | 1.0 |
Token.Token(String, String) | 0.0 | 1.0 | 1.0 | 1.0 |
Token.toString() | 0.0 | 1.0 | 1.0 | 1.0 |
Total | 421.0 | 168.0 | 278.0 | 329.0 |
Average | 5.197530864197531 | 2.074074074074074 | 3.432098765432099 | 4.061728395061729 |
优化(Bug)相关
只做了(如上)合并的优化
因为(随手)优化反倒被坑了两次
hw2 由于负值外置考虑不周到而寄
hw3 由于 sin(0) 优化(可能)破坏了架构所维护的规则导致输出不合规
所以 优化=bug ,大家还是不要优化的好。。
发现 Bug 策略
评测机嗯跑 / 群友数据嗯交
在发现 RE 等时会通过分析代码来判断具体bug位置
在发现 WA 时简化测试样例
Format Error 没有测试。
心得体会
先复述一下上面说过的。。
因为过度集中的设计导致 MonoProd 和 MonoSum 类都具有超出预期的复杂度
下次还是不脱离面向对象思想在数据结构上搞 trick 了。。。
会尽可能的贴近课程组理想的架构模式
另外是个人确实在设计模式这方面有所欠缺,回去好好复习课程内容(哭)
以上是关于BUAA-OO Unit1 总结的主要内容,如果未能解决你的问题,请参考以下文章