301
社区成员
发帖
与我相关
我的任务
分享| 类名 | 属性个数 | 方法个数 | 代码规模 |
|---|---|---|---|
| Mainclass | 0 | 0 | 22 |
| Token | 2 | 2 | 29 |
| Lexer | 3 | 4 | 79 |
| parser | 1 | 4 | 132 |
| Expr | 1 | 13 | 171 |
| Term | 5 | 19 | 188 |
| Factor | 0 | 0 | 4 |
| NumFactor | 1 | 7 | 40 |
| VariFactor | 1 | 6 | 32 |
| ExprFactor | 2 | 1 | 35 |
| ExpoFactor | 1 | 6 | 53 |
| DeFactor | 1 | 1 | 12 |
| Function | 3 | 2 | 24 |
| FuncFactor | 2 | 3 | 65 |
| 方法名/所属类 | 方法规模 | 方法控制的分支数目 |
| Mainclass类 | ||
| main | 11 | 0 |
| Token类 | ||
| getType | 1 | 0 |
| getContent | 1 | 0 |
| Lexer类 |
|
|
| getNum | 18 |
2 |
| next | 1 | 0 |
| getToken | 1 | 0 |
| notEnd | 1 |
0 |
|
Parser类 | ||
| ParseExpr | 28 | 5 |
| ParseTerm | 20 | 3 |
| parserFactor | 47 | 11 |
| getNum | 13 | 3 |
| getTerms | 1 | 0 |
| isSimple | 8 | 1 |
| equals | 11 | 2 |
| addTerms | 1 | 0 |
| sort | 20 | 2 |
| add | 6 | 0 |
| mul | 6 | 0 |
| simplify | 10 | 1 |
| diffFunction | 7 | 0 |
| diff | 1 | 0 |
| SimToString | 40 | 11 |
| toString | 1 | 0 |
| Term类 | ||
| isNum | 1 | 0 |
| isVari | 1 | 0 |
| isExpo | 1 | 0 |
| isFactor | 1 | 0 |
| getNumFactor | 1 | 0 |
| getVariFactor | 1 | 0 |
| getExpoFactor | 1 | 0 |
| isSimple | 1 | 0 |
| isZero | 1 | 0 |
| isSimilar | 2 | 0 |
| equals | 3 | 0 |
| isPos | 1 | 0 |
| addFactor | 11 | 5 |
| simplify | 18 | 2 |
| diff | 8 | 0 |
| mul | 6 | 0 |
| add | 5 | 0 |
| compareTo | 1 | 0 |
| toString | 28 | 7 |
|
NumFactor类 | ||
| toString | 6 | 2 |
| equals | 1 | 0 |
| getNum | 1 | 0 |
| mul | 1 | 0 |
| add | 1 | 0 |
| isOne | 1 | 0 |
| VariFactor类 | ||
| getIndex | 1 | 0 |
| mul | 1 | 0 |
| isConstant | 1 | 0 |
| toString | 6 | 2 |
| equals | 1 | 0 |
| ExprFactor类 | ||
| simplify | 15 | 2 |
| ExpoFactor类 | ||
| isConstant | 1 | 0 |
| equals | 1 | 0 |
| getExpr | 1 | 0 |
| isSimple | 1 | 0 |
| mul | 1 | 0 |
| toString | 5 | 2 |
|
DeFactor类 | ||
| simplify | 1 | 0 |
| Function类 | ||
| getExpression | 1 | 0 |
| getParas | 1 | 0 |
| FuncFactor类 | ||
| parseFuncs | 11 | 1 |
| simplify | 10 | 0 |
| replace | 23 | 2 |

可以看到Lexer类和Parser类的平均复杂度圈较高,因为在Lexer类中需要对输入的表达式进行处理将其转化成Token序列,Parser类对Token序列进行递归下降时需要判断因子的具体类型,都涉及到大量的if判断导致复杂度较高。
而Expr与Term类中包含许多对表达式进行运算和化简的函数,因此总复杂度较高。
UML类图:

Token类定义了表达式中最小词元。
Type类为Token类的内部类,用于记录该词元的类型。
Lexer类用于记录由最小词元组成的表达式,并提供遍历的方法。
Parser类用于解析Lexer对象,生成Expr对象。在解析表达式时使用递归下降法,定义Expr类的属性为Term对象的一个序列,Term属性为Factor对象。在解析表达式即parserExpr函数中,根据"+\-"号将表达式分解为项并调用parserTerm函数解析。在解析项即parserTerm函数中,根据“*”号将其分解为因子并调用parserFactor函数解析。
Factor为接口,给出了字符串转化和化简的抽象方法。VariFactor, NumFactor, ExprFactor三个类实现了Factor接口,内部定义了相应的运算和化简方法。
在第一次作业中需要将表达式最终化简为一个关于x的多项式。对于最后化简的结果,每一个基本项都可以表示为一个常数因子与一个幂函数因子的乘积,即形如"a*x^b"的形式。除了这两个因子,一个项还可能包含表达式因子。在将输入的表达式读取完成后再对其进行化简,将表达式中每个含有表达式因子的项化简为多个基本项,从而实现了去括号。
UML类图:

第二次作业新增了以下类:
Function类用于记录自定义函数。
FuncFactor类用于对输入表达式中出现的自定义函数进行处理。
ExpoFactor类用于表示指数函数因子。
在第二次作业中引入了自定义函数与指数函数。对于自定义函数我选择在预处理时进行字符串的替换。由于引入了新的指数函数,此时的基本项变为"a*x^b*exp(<expr>)"的形式,其中expr为化简后的表达式。在Term类中增加新的属性ExpoFactor对象。在表达式化简时,需要对表达式的每一项化简,而化简每一项时先对指数函数因子中表示式进行化简,通过此过程递归地实现了化简表达式。
UML类图:

第三次作业新增了DeFactor类,用于表示求导因子。
在第三次作业中引入了求导因子。本次作业的基本项和第二次作业相同。在Term类中增加新的属性DeFactor对象,在化简表达式时将求导因子求导后化为表达式添加到所属Term对象中,问题就变成了第二次作业中的情况。而在对求导因子进行求导操作时首先要将其中表达式中每一项的求导因子进行求导操作,于是通过此过程递归地实现了对求导因子的求导操作。
在之后的迭代中可能会引入新的因子,如三角函数因子等。在进行迭代时只需要完成以下工作:
在第二次强测中出现了两个bug:
1.一开始对于自定义函数的处理中我新建了一个自定义函数类并添加到Term类的属性中,在化简时将自定义函数表达式中的形参替换为实参后以替换后的字符串作为新的表达式对其进行解析,最总得到一个不含有自定义函数的表达式因子将其添加回Term对象中。这种方法由于复杂度太高,时间与空间的开销都比较大导致几个数据出现了TLE和RE。
2.最初在自定义函数替换的过程中直接在函数表达式中查找形参的位置并替换为实参,导致忽略了这种情况:
1
f(y,x)=x+2*y
f(x,x*2)
此时将y替换为x后再进行x的替换时会将刚刚由形参y替换为的x当作形参,从而导致错误的结果。
因此我对自定义函数表达式进行了预处理,将其中所有形参后面加上该函数名,从而在查找能够区分实参与形参。
在互测中先使用自己在debug时出现问题的数据进行测试,同时可以构造一些测试边界情况的数据,再使用测评机进行测试。
1.在表达式化简后输出前进行同类项合并,将系数为0的项去除,并对所有项按照幂函数的指数从大到小排序。在输出时首先搜索项中系数大于0的项将其输出,这样使得在输出为“1-x”这种情况时使得表达式的长度更短。
2.在预处理中从后往前替换自定义函数因子,从而实现了对函数嵌套情况的处理。
3.对自定义函数表达式进行预处理,将其中的形参前加上占位符,保证了后续的替换不会出错。
这三次作业总体难度适中,在设计的过程中我关注到了一些之前忽略的点,比如构造函数中对象的赋值问题,如果不进行克隆直接赋值可能会引发一些问题。一个方法中是选择返回一个新的对象还是直接改变该对象的属性而不返回等。在最初写项与表达式计算的方式时我选择了直接改变对象的属性而不返回,后来发现在表达式相乘时,表达式中每一项会进行多次运算,这种写法会使得项在第一次运算时就被改变,而导致后续运算出错。通过完成作业,我更深刻的理解了这些问题以及不同情况下选择哪种方法更合适。
同时我还认识到一个好的架构的重要性,一个好的架构可以提高代码的可读性,可维护性,同时在后续迭代中的只需添加新增的功能而不用大幅修改原来的代码,极大地节约了时间。
在课程设计上未来的作业中可以为同学们多提供一些框架,以让同学们更好地体会到好的框架在后续迭代过程中的重要性。