301
社区成员
发帖
与我相关
我的任务
分享 第一单元的主要任务是完成表达式的解析和化简,本单元最终完成的作业的UML图如下:

本任务先才采用递归下降法对表达式的结构进行建模,然后再利用建模出的结构进行计算。主要对于不同层级的表达式(Expr)、项(Item)、因子(Factor)分别建立对象,值得一提的是因子(Factor)层级有很多子类:表达式因子(ExprFactor)、指数函数因子(ExpFactor)、常数因子(ConstFactor)、求导因子(DerivativeFactor)和PowFactor(幂函数因子),本任务中分别将其抽象成了对应的对象。这里的对象只负责接受递归下降语法分析器的分析建立相应的层次结构,计算的时候调用cal()方法得到每个部分化简后的结果,结果统一采用多项式(UnitList)的形式,其是单个单项式(Unit)的加和。
每个单项式表示:
$$
coef * x^{xpower}*e^{epower}
$$
其中coef是BigInteger, xpower是long, epwoer为多项式(UnitList)
这样虽然能够表示出每个层次的计算化简结果,但是也导致了Unit和UnitList耦合度较高
为了方便计算化简,本任务还实现了Unit和UnitList的常用方法如求导、相加、判断是否为同类项等
其中判断是否为同类型并没有很严格的完成,只采用了判断toString是否相等这种简单粗暴的办法,其实忽略了很多情况,例如1+x和x+1会被判断为不是同类型,存在很大的改进空间,如果想改进可以重写Unit和UnitList和Unit的equals()和hashCode()方法,或者在UnitList中改采用HashMap来存储Unit。
本任务采用预处理字符串替换的方式来处理自定义函数,涉及到的细节很多。比如exp中的x会和变量的x混淆,因此预处理将exp替换为E,对于f(x) = 2 + x , 2*f(x)来说,直接替换变成 2 * 2+x 会出现计算优先级的问题,因此在替换完成后的整体需要加括号,同样的原因对于替换的参数也应该加括号。
本任务的代码长度分析如下:

比较复杂的是实现自定义函数提花你的Function类和实现计算的Unit类和UnitList类,值得改进的是本次作业的注释太少了,平时还是应该养成写注释的习惯。
| ConstFactor.cal() | 0.0 | 1.0 | 1.0 | 1.0 |
|---|---|---|---|---|
| ConstFactor.ConstFactor(BigInteger) | 0.0 | 1.0 | 1.0 | 1.0 |
| ConstFactor.toString() | 0.0 | 1.0 | 1.0 | 1.0 |
| DerivativeFactor.cal() | 0.0 | 1.0 | 1.0 | 1.0 |
| DerivativeFactor.DerivativeFactor() | 0.0 | 1.0 | 1.0 | 1.0 |
| DerivativeFactor.DerivativeFactor(Expr) | 0.0 | 1.0 | 1.0 | 1.0 |
| DerivativeFactor.toString() | 0.0 | 1.0 | 1.0 | 1.0 |
| ExpFactor.cal() | 1.0 | 1.0 | 2.0 | 2.0 |
| ExpFactor.ExpFactor(Factor, int) | 0.0 | 1.0 | 1.0 | 1.0 |
| ExpFactor.toString() | 1.0 | 2.0 | 2.0 | 2.0 |
| Expr.addItem(Item) | 0.0 | 1.0 | 1.0 | 1.0 |
| Expr.cal() | 2.0 | 1.0 | 3.0 | 3.0 |
| Expr.neg() | 0.0 | 1.0 | 1.0 | 1.0 |
| Expr.toString() | 1.0 | 1.0 | 2.0 | 2.0 |
| ExprFactor.cal() | 1.0 | 1.0 | 2.0 | 2.0 |
| ExprFactor.ExprFactor() | 0.0 | 1.0 | 1.0 | 1.0 |
| ExprFactor.ExprFactor(Expr, int) | 0.0 | 1.0 | 1.0 | 1.0 |
| ExprFactor.toString() | 1.0 | 2.0 | 2.0 | 2.0 |
| Factor.cal() | 0.0 | 1.0 | 1.0 | 1.0 |
| Function.addFunction(char, String, String) | 0.0 | 1.0 | 1.0 | 1.0 |
| Function.addFunction(String) | 1.0 | 2.0 | 1.0 | 2.0 |
| Function.Function() | 0.0 | 1.0 | 1.0 | 1.0 |
| Function.replace(String) | 1.0 | 1.0 | 2.0 | 2.0 |
| Function.replaceFunction(String, char, String, String) | 36.0 | 1.0 | 10.0 | 12.0 |
| Item.addFactor(Factor) | 0.0 | 1.0 | 1.0 | 1.0 |
| Item.cal() | 5.0 | 1.0 | 4.0 | 4.0 |
| Item.neg() | 0.0 | 1.0 | 1.0 | 1.0 |
| Item.toString() | 1.0 | 1.0 | 2.0 | 2.0 |
| Lexer.Lexer() | 0.0 | 1.0 | 1.0 | 1.0 |
| Lexer.Lexer(String) | 0.0 | 1.0 | 1.0 | 1.0 |
| Lexer.nextExpr() | 13.0 | 1.0 | 8.0 | 10.0 |
| Lexer.nextFactor() | 18.0 | 12.0 | 6.0 | 12.0 |
| Lexer.nextItem() | 7.0 | 1.0 | 6.0 | 7.0 |
| Main.main(String[]) | 31.0 | 1.0 | 16.0 | 18.0 |
| PowFactor.cal() | 0.0 | 1.0 | 1.0 | 1.0 |
| PowFactor.PowFactor() | 0.0 | 1.0 | 1.0 | 1.0 |
| PowFactor.PowFactor(int) | 0.0 | 1.0 | 1.0 | 1.0 |
| PowFactor.toString() | 1.0 | 2.0 | 1.0 | 2.0 |
| Unit.add(Unit, Unit) | 0.0 | 1.0 | 1.0 | 1.0 |
| Unit.derivate(Unit) | 4.0 | 2.0 | 3.0 | 4.0 |
| Unit.isPositive() | 0.0 | 1.0 | 1.0 | 1.0 |
| Unit.isSimilar(Unit, Unit) | 1.0 | 2.0 | 1.0 | 2.0 |
| Unit.isZero() | 0.0 | 1.0 | 1.0 | 1.0 |
| Unit.mul(int) | 0.0 | 1.0 | 1.0 | 1.0 |
| Unit.mul(Unit, Unit) | 0.0 | 1.0 | 1.0 | 1.0 |
| Unit.neg() | 0.0 | 1.0 | 1.0 | 1.0 |
| Unit.toString() | 5.0 | 1.0 | 4.0 | 4.0 |
| Unit.Unit() | 0.0 | 1.0 | 1.0 | 1.0 |
| Unit.Unit(BigInteger) | 0.0 | 1.0 | 1.0 | 1.0 |
| Unit.Unit(int) | 0.0 | 1.0 | 1.0 | 1.0 |
| Unit.Unit(UnitList) | 0.0 | 1.0 | 1.0 | 1.0 |
| UnitList.add(Unit) | 10.0 | 4.0 | 6.0 | 7.0 |
| UnitList.add(UnitList, UnitList) | 1.0 | 1.0 | 2.0 | 2.0 |
| UnitList.clone() | 0.0 | 1.0 | 1.0 | 1.0 |
| UnitList.derivate(UnitList) | 1.0 | 1.0 | 2.0 | 2.0 |
| UnitList.isEmpty() | 0.0 | 1.0 | 1.0 | 1.0 |
| UnitList.isEqual(UnitList, UnitList) | 3.0 | 3.0 | 2.0 | 4.0 |
| UnitList.isZero() | 3.0 | 3.0 | 2.0 | 3.0 |
| UnitList.mul(int) | 1.0 | 1.0 | 2.0 | 2.0 |
| UnitList.mul(UnitList, UnitList) | 3.0 | 1.0 | 3.0 | 3.0 |
| UnitList.neg() | 1.0 | 1.0 | 2.0 | 2.0 |
| UnitList.toString() | 7.0 | 4.0 | 3.0 | 5.0 |
| UnitList.UnitList() | 0.0 | 1.0 | 1.0 | 1.0 |
| UnitList.UnitList(Unit) | 0.0 | 1.0 | 1.0 | 1.0 |
| Total | 161.0 | 91.0 | 137.0 | 160.0 |
| Average | 2.515625 | 1.421875 | 2.140625 | 2.5 |
虽然总体圈复杂度不算特别高。但还是可以看出圈复杂度最高的部分在于词法分析器对表达式层次的解析和字符串替换的过程,这两个过程本身逻辑就比较复杂,if和循环也比较多,主要是很多都重复地实现了括号匹配的,根据研讨课上某位同学分享的可以为括号匹配专门编写一个实现类感觉是一个可以有效降低圈复杂度的思路。
在本次任务中主要遇到了如下几个bug
错误优化表达式括号
例如Exp(2+x),其中2+x只能作为表达式因子,括号不可去,应该输出Exp((2+x))
指数采用int存储溢出
没有正确的分析数据范围,因为存在括号嵌套从而可能导致指数非常大,应该采用BigInteger存储
符号处理不正确
在处理项前面的符号的时候忘了index++,从而导致符号没有正确被处理
在本次任务中合并同类项的优化做得不太完全,但还做了一些其他简单的小优化
为了保证Exp中表达式有正确的括号,本任务暴力的将所有表达式都加了括号,但正则查找了形如Exp((+-number))的去除了括号,降低了长度
为了减少长度,在表达式中优先输出正数,如将-3+x改为x-3,长度降低了1
本次OO单元作业完成情况不太理想,最开始搞错了ddl的时间没及时提交,第三次作业的bug修复过程也忘记了提交,希望能合理安排时间,认真及时完成之后的每次oo作业。
在单元作业的编写中,发现了在每个类内部加一个main方法就可以独立的测试每个类,这个可以比较方便快捷的实现对类的测试,在多次debug中也明白了单元测试的重要性,如果代码写完了才来一起测试就算找出问题也很难确定bug的位置,就算确定了bug的位置也很难调试。同时在编写程序的过程中我也明白了设计的重要性,最开始设计字符串替换的时候考虑得不够全面,漏掉了很多特殊情况,从而导致最后一而