1.第一次作业
1.1 题目分析
1.2 完成过程
通过正则表达式对输入数据的格式进行规范,用正则表达式写出每一项的规范,再将其作为分割标准,如果分割后产生的字符串数组中含有不为空的元素,就会判定为"WRONG FORMAT!",再利用正则将处理后的字符串中寻找项,将其系数与指数保存下来,最后在进行合并处理输出。第一次作业没有考虑延展性,完全是面向过程,一Main到底,只是打了几个函数块帮助处理,程序UML图示如下:
1.3 结构分析
本次作业的度量情况如下:
代码总长度167行
方法个数与长度、控制语句数量等信息如下图:
最后的化简放在printf里面实现了,通过遍历的方式读取每一个项,再根据系数与指数的大小状况进行特殊判断处理;长度即将突破60行,main函数主方法的长度刚好60行,冗长的代码带来的是极大复杂度,这也是在提醒尽量把写一个”大“方法块换成分割成若干个更细致的方法;对于本题处理的方法的优点就是:简单,缺点就是:很难复用(因为基本就是面向过程的做法)
1.4 Bug测试
因为本次作业我才用正则表达式的方法对输入的字符串进行处理,将一个表达式完全换成正则表达式进行匹配的情况会有爆栈的情况发生,我改用了写出一个项的表达式进行分析,但是正则的复杂度还是很高,不易于他人理解,而且很容易就会漏掉一些如空格之类的情况,当如 ?等判断符号累加起来时,可能出现的匹配情况会成倍提升,这也给分析逻辑的准确性带来了极大的不便,在互测环节中也发现了利用正则表达式的同学的一些神奇的bug(不认真仔细去分析正则你的情况感觉想不到会出现这样的bug)。这里的收获是尽量少打很长的正则表达式,越长的正则逻辑越容易出问题。
2.第二次作业
2.1 题目分析
第二次作业相比第一次多了三角函数 sin(x) 与 cos(x)的求导,且可满足一个项由多个因子相乘我做的优化只有合并同类项后将 sin(x)^2 + cos(x)^2 = 1 的情况化简。
2.2 实现过程
在第二次作业中,对数据的读入处理仍然是通过正则表达式来进行判断,分割出一个个项后在用乘号进行分割得到若干因子,这里创建了一个对象为合并后项,因为本次作业的 sin(x) 与 cos(x) 结构类型很简单所以并没有另开一个类,只是提取指数,将一个项合并同类项后就是一个 Struct 类,规定好格式后求导就很简单了,程序uml图如下:
struct类构建的优点就是求导的实现很简单,只需要按照已经设定好的格式输出就好了;缺点也很明显,难以进行扩展,在第三次作业的时候就需要重构了
每个struct类求导后最多可以生成三个struct类,将求导生成的类都放入到一个Arraylist里面,在放入数组之前首先遍历数组里面已经有的struct类,看是否可以进行sin^2 + cos(x)^2 化简,进行化简的标准就是比较sin与cos的指数,假定struct a 的sin cos指数分别为 s1 c1 另一个为 s2 c2,易知当 s1-2 == s2 && c1 == c2 - 2 (或者反过来),但是在某些情况的时候化简后长度会变长(如当指数0减2后会变成-2,此时输出长度不减反增,或者在合并的时候系数发生的变化等等),有关这样长度的变化情况过于复杂,所以首先模拟化简后的长度,与不化简的长度进行比较,如果变短则采用化简,否则不采用。最后转换成字符串形式采用replacea的方法去除指数为0为1等情况的处理。
2.3 结构分析
在求导的过程中的合并同类项过程用了过多的循环,在最后输出的时候用了太多的replaceall得不偿失了
2.4 bug分析
这次作业犯了一个及其愚蠢的错误,本来意识到自己的化简过程会受到表达式的先后顺序问题,而且只是一次循环并不一定能够完成化简,实在没想到什么好的办法,周二晚上就直接莽了个三次循环(本来Derivation中last就已经是最后要输出的数组了,但是你可以看到last2以及last3),然后剧本来了 首先喜闻乐见地打错了last3的遍历条件(手抖少打一个字符),又喜闻乐见地没测出来这个问题,结果喜闻乐见地强测出现了几个数据跑崩了抛出爆栈电视剧都不敢这么演 。在互测环节,仍是在正则表达式中发现了一些问题,都是看到了超长正则表达式出现问题;还有就是在replaceAll 进行一些简单化简的时候的问题,这里也是吸取了教训,本来是图replaceall简单,但是在实际使用的时候发现不得不去考虑很多边界情况,如单单replaceAll("\\^1","") 就会出现将x^11 出现 x1的情况,而且在输出的格式不固定的情况下,出现的情况会更加难以想到与控制。在处理多个情况的时候,该方法的复杂度又会特别高,实在得不偿失。
3.第三次作业
3.1 题目要求
第三次作业加入了表达式因子的概念,允许嵌套
3.2 完成过程
这次作业的表达式变得更加复杂,因为括号嵌套的原因难以用正则直接去规范一个项的情况,但是感觉脱离正则而使用字符串去判断输入太过冗长麻烦,所以这里仍然想使用正则表达式去进行小范围的wf,利用一个求导接口来使不同类在求导的时候可以统一处理,使用一个类似工厂的类去判断读入字符串的类别并创造相应的类返回,之后进行递归求导。这里因为之前的x sin cos 没有分成单独的类,所以这次相较于第二次重构。程序UML图如下:
对于输入的处理,初步再wf模块中对空格的几种wf情况罗列出来,再将括号里面的“+ - * ^”符号全部换掉,先将多余的符号去掉,将“-”转换成“+-”,然后再用“+”号去分割得到若干个项,再用分“*”割得到若干个因子,使用递归下降与正则分析括号内的东西是否合法,合法就传入工厂classbuild 去生成对应类,用接口derivation类统一化求导过程,具体的递归求导过程在类sin cos中打出。有第二次作业的失误,且这次的作业性能分只占5%,厚颜无耻地基本没有什么特别的优化。打成字符串最后直接输出。输入将括号里的运算符号换成其他符号处理的优点是:分割项与因子变得极其简单,很容易就可以提取出对应的元素。缺点就是:为了实现这一操作实现不得不对字符串进行一些预处理,如将多余的符号替换成最简式,对于某些情况会有问题,如 sin(-x) 这一数据中的本没有错误,但是会将“-”替换“-+”进行处理以便操作但是对sin(+-x)这一种本来就会错误的数据不得不进行预先特殊判断,而需要这样的特殊判断的细节如果少考虑掉就会成为bug所在,是比较麻烦的地方。有关类的构建继承同一个求导接口就是这样可以归一化处理很方便。
3.3 结构分析
代码总长度:547行
具体方法数、方法复杂度、耦合度等数据如下图:
3.4 bug分析