301
社区成员
发帖
与我相关
我的任务
分享
我所设计的框架整体分为三部分:
第一部分为数据处理部分,这部分处理集中在Processor类的方法中。首先是对输入进行预处理,对自定义函数进行实参替换后替换到表达式中,然后对表达式进行去除空白字符和多余符号的处理。以及对最后所得到的展开后的表达式进行同类项合并化简。
第二部分为解析部分,使用的策略为递归向下,参考了训练部分运用Lexer类解析词法,Parser类根据形式化表述来定义与解析语法,这与操作系统中的词法分析和语法分析相似。
第三部分则是MainClass进行数据的输入输出,并在三次作业中进行迭代。
| Method | CogC | ev(G) | iv(G) | v(G) |
|---|---|---|---|---|
| factor.Exp.clone() | 0.0 | 1.0 | 1.0 | 1.0 |
| factor.Exp.Exp(Factor) | 0.0 | 1.0 | 1.0 | 1.0 |
| factor.Exp.toString() | 2.0 | 2.0 | 2.0 | 2.0 |
| factor.Expr.addTerm(Term) | 0.0 | 1.0 | 1.0 | 1.0 |
| factor.Expr.clone() | 1.0 | 1.0 | 2.0 | 2.0 |
| factor.Expr.expand() | 1.0 | 1.0 | 2.0 | 2.0 |
| factor.Expr.Expr() | 0.0 | 1.0 | 1.0 | 1.0 |
| factor.Expr.getTerms() | 0.0 | 1.0 | 1.0 | 1.0 |
| factor.Expr.mergeExpr(Expr, Expr) | 2.0 | 3.0 | 1.0 | 3.0 |
| factor.Expr.toString() | 3.0 | 1.0 | 3.0 | 3.0 |
| factor.Number.clone() | 0.0 | 1.0 | 1.0 | 1.0 |
| factor.Number.Number(String) | 0.0 | 1.0 | 1.0 | 1.0 |
| factor.Number.toString() | 0.0 | 1.0 | 1.0 | 1.0 |
| factor.Term.addFactor(Factor) | 0.0 | 1.0 | 1.0 | 1.0 |
| factor.Term.clone() | 1.0 | 1.0 | 2.0 | 2.0 |
| factor.Term.expand() | 15.0 | 4.0 | 7.0 | 7.0 |
| factor.Term.mergeTerm(Term, Term) | 2.0 | 3.0 | 1.0 | 3.0 |
| factor.Term.Term() | 0.0 | 1.0 | 1.0 | 1.0 |
| factor.Term.toString() | 3.0 | 1.0 | 3.0 | 3.0 |
| factor.Var.clone() | 0.0 | 1.0 | 1.0 | 1.0 |
| factor.Var.toString() | 0.0 | 1.0 | 1.0 | 1.0 |
| factor.Var.Var(String) | 0.0 | 1.0 | 1.0 | 1.0 |
| MainClass.main(String[]) | 1.0 | 1.0 | 2.0 | 2.0 |
| parser.Lexer.getChar() | 1.0 | 1.0 | 1.0 | 7.0 |
| parser.Lexer.getCurString() | 0.0 | 1.0 | 1.0 | 1.0 |
| parser.Lexer.getCurToken() | 0.0 | 1.0 | 1.0 | 1.0 |
| parser.Lexer.getExp() | 0.0 | 1.0 | 1.0 | 1.0 |
| parser.Lexer.getNumber() | 2.0 | 1.0 | 3.0 | 3.0 |
| parser.Lexer.getPow() | 0.0 | 1.0 | 1.0 | 1.0 |
| parser.Lexer.Lexer(String) | 0.0 | 1.0 | 1.0 | 1.0 |
| parser.Lexer.next() | 5.0 | 2.0 | 3.0 | 8.0 |
| parser.Parser.checkPos() | 2.0 | 3.0 | 3.0 | 3.0 |
| parser.Parser.expandPower(Term, Factor) | 2.0 | 1.0 | 3.0 | 3.0 |
| parser.Parser.parseExpr() | 4.0 | 1.0 | 4.0 | 4.0 |
| parser.Parser.parseFactor() | 1.0 | 1.0 | 1.0 | 7.0 |
| parser.Parser.Parser(Lexer) | 0.0 | 1.0 | 1.0 | 1.0 |
| parser.Parser.pa | ||||
| rseTerm() | 6.0 | 1.0 | 4.0 | 4.0 |
| parser.Processor.addFunc(String) | 0.0 | 1.0 | 1.0 | 1.0 |
| parser.Processor.merge(Expr) | 0.0 | 1.0 | 1.0 | 1.0 |
| parser.Processor.preProcess(String) | 6.0 | 1.0 | 4.0 | 4.0 |
| parser.Processor.replace(String, String) | 23.0 | 1.0 | 9.0 | 13.0 |
| parser.Processor.replaceFun(String) | 1.0 | 1.0 | 2.0 | 2.0 |
| Total | 84.0 | 53.0 | 83.0 | 108.0 |
| Class | OCavg | OCmax | WMC |
|---|---|---|---|
| factor.Exp | 1.33 | 2.0 | 4.0 |
| factor.Expr | 1.86 | 3.0 | 13.0 |
| factor.Number | 1.0 | 1.0 | 3.0 |
| factor.Term | 2.83 | 7.0 | 17.0 |
| factor.Var | 1.0 | 1.0 | 3.0 |
| MainClass | 2.0 | 2.0 | 2.0 |
| parser.Lexer | 2.875 | 8.0 | 23.0 |
| parser.Parser | 3.67 | 8.0 | 22.0 |
| parser.Processor | 3.6 | 10.0 | 18.0 |
| parser.Tokentype | 0.0 | ||
| Total | 105.0 |
有一些方法的复杂度明显较高。 parser.Processor.replace(String, String)的复杂度高是因为其中包含了大量判断语句来对自定义函数进行替换,而其他的复杂度较高的方法也是因为判断语句和循环语句较多。事实上,对于复杂度这方面我的关注度还是不够,思维还处于直接暴力进行问题的求解,有些地方可以通过减少循环中的判断或者通过多态等方法来减少复杂度。因此在以后的作业中,我应更多的的关注方法的复杂度,对程序进行更好的优化。
第一次作业相对简单,主要是考虑对架构的设计以减少之后迭代的工作量。
通过递归下降的思想,用Processor类实现数据的预处理和计算。用Factor 及其实现类和 Term,Parser 和 Lexer 来进行表达式的解析。整体没有出现很多问题,并在这一步就实现了括号的嵌套。
优化部分在最后通过Processor类中的方法合并同类项来进行优化,减少输出的长度。
依据第一次作业搭建好的基础,只需加入exp因子和自定义函数。
对于exp因子,只需建立新的类和相应的方法。
对于自定义函数,因为不会出现自定义函数之间的调用,我采取的方法是在输入预处理阶段进行自定义函数的替换,替换后和作业1基本相同,改动不多。但要注意在替换中自定义函数形参和变量x之间的替换可能会导致错误替换,我采取的方法是对exp进行替换为e最后再替换回来。
新加入了求导算子,只需在因子类中添加求导的计算方法即可,无需进行很大的改动。
对于自定义函数可以相互调用部分,我在替换中进行了重复判断替换,复杂度较高后续有更多的优化空间。
目前仍存在如下的问题可以进行优化。
同类项合并部分,我采取的方法只是简单的判断各个项之间的因子是否相同来判断是否合并,并且只在最后展开后进行判断,复杂度较高。可以考虑在每一个表达式因子进行展开后就进行一次同类项合并,并探索其他更简洁的方法进行同类项合并。
自定义函数替换部分,在预处理阶段进行自定义函数的替换。在第三次作业的中因为能够互相调用,因此复杂度较高,应探索更为简洁的方法来进行自定义函数的处理,可以考虑将其作为因子在递归下降解析中进行处理。
通过一个单元的作业的迭代,我明白了良好的架构的重要性。应在设计的初期就想好未来可能的拓展方向,架构的拓展性如果不够,之后大概率会重构,十分耗费时间,因此前期将时间多花在架构上是很重要的。
同时我还明白了应多注意类和方法的复杂度,更多的以面向对象的方法来进行代码的构建,而不是暴力解决问题,在以后的作业中应多多注意这点。