OO第一单元总结

OO第一单元总结

第一单元的三次作业主要是对给定输入表达式的解析、提取、存储以及化简输出,从第一次到第三次作业,需求不断增加,我们需要在需求变化的过程中对代码进行修改,扩展已有的类、增加必要的类、接口,甚至要在当前架构难以满足新任务的需求时进行重构。个人感觉表达式处理在学习编译原理课程后在回溯会觉得轻松不少。整体考虑后决定采用Normal Mode。

作业一

输入一个表达式,表达式包括加、减、乘、乘方运算,其中乘方作用的对象为单个变量或表达式,且指数只能为0到8的整数。

  • 代码分析

  •    复杂度分析
  •  

    • UML图

 

本次作业要求为解析表达式并化简,其中表达式由项通过加减法连接,项由因子(变量,常量,表达式)通过乘法连接。故本次作业分为两个模块,一是解析表达式,使用递归下降的办法构造表达式树;二是拆括号,合并同类项化简。

Main是主类,Parse是语法分析器, 采用递归下降法对表达式进行语法分析,Expression类在构造时就进行表达式解析,首先去除空格和多余的加减号,替换'**'以防解析错误。然后通过加减号分割,递归下降解析。

  1. 分析题意可以很容易看出表达式-项-因子的层级关系,考虑到因子中表达式因子的设定,故采取递归下降的解析方案——设定读取与解析两个模块,并行操作。不断读入表达式,对于运算符/常数/变量判断后分别处理输入,输入模块将输入的字符串投入解析模块。解析模块自顶到下递归调用。

  2. 在解析过程中,对于表达式-项的存储,采用Arraylist;在项-因子的存储中,采用Hashmap<factor,index>,即为三种因子统一设置factor父类。

    作业二、三

    由于第二三次作业整体代码实现差别不大,故放在一起。

    • 代码分析**

    • 复杂度分析
    •  

      • UML图

    作业增加了Sin和Cos, 还出现了自定义函数的情况。根据其语法成分定位令其继承Factor。关键在于自定义函数和求和函数的处理, 根据指导书这两者在语法成分定位上是作为因子存在; 但是由于内部存在加法操作, 故最终应该转换为一个表达式因子. 采用了代入的方法避开暴力替换很可能出现的各种问题。归结起来与第一次区别就是:

    • 存储结构:引入了Function抽象类,由Power、SinCosBaseFactor、Product等类继承。继承同时再让SinCos实现Factor`接口

    • 展开括号:基本和上次一样,除了sincos由于我支持了内部是表达式因子,于是我发现因子是三角函数且内部是一个表达式因子就要进去展开,化简,如果还是一个sincos那么继续往里面搜索

    • 一些细节的处理:自定义函数作因子替换时注意增加必要的括号。另外,由于三角函数类中存储因子的形式就是Poly多项式类,故在多项式类中设置一个标志数据,标志该对象是否为三角函数的因子,并结合其他条件在本类的toString方法中进行输出。

    • 优点

      • 本次作业对于括号嵌套的处理比较完美实现了统一的运算方式和因子的统一生成和储存。

      • 建立了表达式树的结构,对于自定义函数及求和函数的因子代入很早就进行了处理有利于debug观察和解耦。

    • 缺点

      • 表达式化简仅仅实现了同类项合并,对于 cos(0)sin(0) 之类的因子没有考虑化简,导致部分测试点性能分数过低,有几个点性能分为0。

    Bug分析

    在中测阶段采用的测试方式如下:

    数据点的生成同样采用了递归下降的方法,可以看作对解析表达式求逆,即递归向下生成表达式。产生的数据使用subProcess.Popen函数重定向输入到jar包中,将两个jar包的输出使用eval函数对拍。但该方法存在缺点,没有对输出的格式的正确性做判断,可以将输出结果输入官方包做判断。还有就是借鉴大佬们的做法,采用边界分析法进行样例构造。

    在强测阶段被Hack和发现了不少bug,比较有代表性的如下:

    • sum(i, -1, 2, xxx) 为考虑到sum的上下限可能为负数, 导致在解析中出错;

    • sin((−x)) 在化简时,没有把-x看作一个term,而是把它看作了factor,属于题目理解的不仔细,对于format没有认真考虑,而在后续写评测机时,没有检验格式的正确,导致了漏网的bug.

  •    心得体会

  • 需要考虑可扩展性:注重可扩展性,第一次作业的拆括号逻辑可以一直用到第三次作业。

  • 对java内存布局及管理仍然概念模糊:最直接的一个障碍就是深拷贝浅拷贝的问题。虽然在做作业的过程中将引用类比C的指针,小心避开了拷贝的陷阱,但是仍然对java内存管理没有很清晰的概念。之后应当把这里搞明白。

  • 自动化测试很重要:在写完代码后,通过大量数据进行覆盖性测试发现问题,并用出问题的数据单步调试发现bug,相比较从头读代码而言效率较高。

posted @ 2022-03-26 02:12  Charlie_Cosmos  阅读(15)  评论(0编辑  收藏  举报