第 1 单元博客
基于度量来分析自己的程序结构
类 | 属性数 | 方法数 |
---|---|---|
CalcMul | 4 | 17 |
CalcSum | 1 | 14 |
CogC | ev(G) | iv(G) | v(G) | |
---|---|---|---|---|
Total | 157 | 166 | 186 | 220 |
Avg. | 1.40 | 1.55 | 1.74 | 2.06 |
类图
Parser, Result, Token, T(?) 部分提供一种表达形式语法的方式。
Tokenize中各对象存储形式语法,Convert存储自定义函数。
Calculate, Calc(?), Function是计算。
分析 BUG
- 第 1 次
- 无
- 第 2 次作业
- sin/cos 内部的因子前出现了正号
不符合因子语法 - 整数-1 的符号错误
toString()中,按分段产生各因子的字符串,常数项绝对值为 1 时直接返回了字符串"1",没有考虑符号。
以上两项都在calculate.CalcMul.toString()
; 圈复杂度 = 8 - sum 语法错误
sum 的语法为"sum" "(" empty "i" empty { "," empty factor_const empty }[2] "," empty sum_expr empty ")"
而处理语法重复项的类中,写了<=号,导致实际匹配了 3 次,而不是 2 次。使得后面 sum_expr 出现语法错误。
parsing.TRep.parse()
; 圈复杂度 = 5
- sin/cos 内部的因子前出现了正号
- 第 3 次
- 无
发现 BUG
很遗憾,没有发现别人的 BUG.
架构设计体验
从开始即确定使用递归下降方法,并按照形式语法实现输入解析。然而我很快发现这样会导致多数编码时间用于实现解析。
于是我改为将语法也用类来实现,这样就能用一句代码描述一条语法,并且能简单对应代码与语法。如表达式:
// expr = empty [sign empty] item {empty sign empty item} empty
expr.setTokenList(empty, new TList(sign, empty).opt(), item, new TList(empty, sign, empty, item).rep(), empty);
这样解析之后得到语法的树,再在语法的树上进行计算。
尝试从语法树上获得不适当的结果类型时会抛出异常,易于定位代码写错的地方。
计算部分分为两个类,一个是项类:内容从A*x**B
发展到A*x**B*sin(...)**C*cos(...)**D
。
一个是表达式类,为因子的合。在计算过程中即展开,合并同类项。
学习体会
有时并不是任务本身有多“难”,而是在编码过程中的大量重复工作导致更容易犯错误。这时有必要自己制造工具来减少这种困难。
即适合造轮子的时候就要果断造。