第一次作业总结与反思
摘要
本单元作业围绕一个核心命题“消除括号”展开,进行了三次的作业迭代:
- HW1:要求展开并合并含有简单幂函数的多项式的括号
- HW2:要求HW1基础上展开并合并新增含有简单多项式、三角函数、自定义函数的多项式的括号
- HW3:要求HW2基础上展开并合并新增含有单项求导因子的多项式的括号
由于第一次到第三次作业之间基本上是增量迭代的关系与架构,所以为了避免冗余的重复,本篇博客计划分别采取横向和纵向的思路来分别展开对代码的架构分析和对增量迭代的思路分析以及反思总结。
####碎碎念
没有学过oopre的选手暴风哭泣.jpg,和oo的作业经历了一段相当艰难的磨合,虽然最后也没有磨合得很好,但是还是有一定的收获的,就酱。
###纵向——代码迭代分析
####第一次作业#####初步形成的基本架构思路
- 采用hashmap作为基本容器
- 借鉴实验代码的架构,使用
Lexer
模块实现表达式分块,使用 Parser
模块实现表达式解析 - 采用递归下降架构,所有的解析元素都继承自
Factor
接口 - 采用正则表达式的方式,在表达式输入的时候就对表达式的符号进行预处理,如符号的化简、**
-
转化为 [+]%
(方便Lexer模块的解析)、符号的替代**(同样是方便Lexer模块的解析)等等
#####表达式简化方法
- 符号保留到最简的形式,比如
**+
简化为 **
、多个符号化简成为一个 - 表达式开头是
-
号的情况下,检索后面的正项放到开头 - 相同项相乘结合成为幂次
ps:在Jerry同学的强烈安利下选择使用Hashmap的容器之后,发现真香啊~只要重写Hashcode和equals方法,即可“自动”实现幂次的结合和同类项的合并
- 同类项合并,同上,不同的地方是,幂次的合并是
Term
层面的 value
值的合并,系数的合并是 Expr
层面的 value
值的合并。
#####作业中遇到的bug以及注意事项
1.没有考虑大数的问题,在程序中所有涉及Number类操作和属性信息移植的过程都应该考虑支持大数的问题。剩下的交给BigInteger吧~
2.一些符号处理的顺序符合一定的顺序关系时,可以更加方便,减少bug。
3.java中的对象赋值类似于C中的指针赋值,注意深度clone。
4.注意0次方的特判处理。
####第二次作业
#####新增的架构思路
- 怎么应对新增的自定义函数因子?本人的一些初步思路在一位的学长的博客中得到印证和更加深层次的启发,最后选取采用类似于表达式树的形式架构。新增了Function类,进行表达式的解析和取代。注意程序的高聚合性。最后解析完成的自定义函数因子就是一个新的
Expr
类型的 Factor
。 - 多提一嘴函数替换的小tips:
- 解析函数形式的时候,为了避免前后解析的冲突问题,可以化为abd(注意'c'和'cos');
- 同时注意xyz的顺序问题:其实我们不关心先出现的是xyz中的哪一个,但凡先出现的就用a代替就行了[]~( ̄▽ ̄)~*。
- 三角函数因子同上。
#####表达式简化方法
- 一些特殊的三角函数计算式,例如sin^2+cos^2=1,sin^2-cos^2=1-2*cos^2等等。
- 三角函数的同项化简,关键是把内部表达式按照一定规则(有点类似范式这种东东)排列,并且规定符号(类似于共线的向量的方向)的规范。
- 前者采用Hashmap的容器,在
toString
过程中就会自动实现排序; - 后者只要规定
toString
处理后式子的开头一定是正号,不是正号就按照三角函数的性质“提取”出来(这里注意cos和偶次幂不用变号)。
#####作业中遇到的bug以及注意事项
更多的问题体现在自身架构上的问题,改成立体、层次化的设计之后,方便很多,架构也清晰了不少。
####第三次作业
#####新增的架构思路
- 函数求导的问题:每一个元素类型都新增了求导方法,并且采用深clone的方法。
#####表达式简化方法 - 函数处理过后就是普通的表达式了,化简的处理方式和前面的作业没有很多新的需求。
#####作业中遇到的bug以及注意事项
1.起初没有做测试题就先构建思路了,考虑到函数迭代的层次问题,还把函数按照层次排序写了个函数qaq。不说了,下一次先做测试题看看输入规范了。
2.很多设计上的细节,比如输出类的方法,最好都要对“空”的项进行特判,以防一些预料之外的bug。
###横向——代码架构分析
允许我手动分析一下三次迭代下来总体的架构。
架构
- 采用Expr-Term-ExprPower-Factor的层次,比较方便于迭代。装入expr包中。
- 本人的整体思路遵循“边读入边解析”。其中Term的Expand方法(展开的核心方法)较为庞大且频繁,可以考虑另外建立一个类,来提高内聚度,降低耦合度。
- 外层采用main Lexer(拆分表达式) Parser(解析表达式) Function Input(简化input,方便后续的解析和化简)
- 其中耦合度相对于第一代“扁平化”的设计,优势明显存在的。
- 优点是具有一定的面向对象的雏形,不再关注实现流程,而重点关注各个“零件”之间的关系,如何封装,又是如何相互关联、相互依赖的。
- 缺点也比较明显,对于java的一些技巧不够熟练,导致遍历Hashmap的Iterator等等方面写得很累赘。同时可继承性质的结构、内部“黑盒子性质”的封装意识也尚不够强烈。
###心得体会&展望未来
- 本次作业中深刻体会到,面向对象编程,尤其是要将对象立体化;上下可以拓展、承接的设计与架构,在三次作业迭代中展现出了极其明显的优势。
- 在表达式化简过程中,深刻体会到Hashmap容器的优势,并且重写Hashcode和equals方法时应该极其小心,哪些应该被解析(这点和前面清晰的架构的需求是依赖关系)。
- 整体的感受是:痛并快乐着。
中二的说法?
"Everying that kills me makes me fell alive."
好像自己很幽默的亚子。