buaa 2023 oo 第一单元总结

OO第一单元总结

第一次作业总结

分析

我们要做的是对多变量的多项式进行括号展开,并进行相应的同类项合并化简。根据training代码给出的提示,个人思路分为以下两步:

  • 将输入的表达式通过递归下降转化成后缀表达式 (展开括号)
  • 将后缀表达式计算并合并同类项输出 (计算后缀表达式)

基本思路

我的代码分为上述的两部分,分别实现相应的功能,用main函数连接

  • 递归下降求后缀表达式

    • 首先对表达式进行预处理

      1. 去掉表达式所有的空白符

      2. 第一项为符号,则在表达式开头补0

      3. 将表达式中连续的+/-号整合成一个+/-号

      4. 去除******后面紧跟着的 +

    • 通过形式化定义,讲表达式分为四个层次:Factor(变量和常量)、Power(幂)、Term(用*号相连)、Expr(用+/-号相连),并对上述四种对象进行解析

      • parseFactor:若下一个字符为单目运算符或者是数字或者是变量 x ,则不断看下一个字符是否是数字。如果不是数字,那么说明下一个符号不属于因子,即对因子的解析结束
      • parsePower:基本项由因子的幂次方组成.当解析到一个因子后,若下一个解析到的单目运算符为 ** ,那么读入这个符号并做处理,下一个符号一定是一个带符号整数,否则,基本项的解析结束。
      • parseTerm:项由幂函数或因子以*号相连,处理方法类似幂函数
      • parseExpr:表达式由项和+/-号相连,处理方法类似
      • 通过题目所给的形式化表述可以表叫容易地建立层次,并且通过方法的互相调用完成解析。笔者将**为一个整体。
      • 可扩展性:递归下降方法的好处在于,它可以通过方法之间的间接递归调用,非常自然地处理嵌套的表达式(即带嵌套括号的表达式)。
    • 例如:2x**3 + 5 被转换成 2 x * 3 * 5 +

  • 后缀表达式计算

    • 第一步处理后得到的表达式,运算符只含有+/-/*/**,操作数有带符号的整数以及变量
    • 新建一个多项式类。由于后缀表达式的优越性,我们对该表达式整体处理。因此我们每读到一个运算符,就对最近的两个多项式进行对应运算并返回一个多项式即可

具体实现

总UML类图

在这里插入图片描述

读取类Lexer

  • 该类主要对原始表达式进行处理,使其按照需求每次都能输出一个操作数/运算符
  • 注意判断数字前的+/-号是否为单目运算符

解析类Parser

  • 该类使用递归下降法处理表达式
  • 注意:若是识别到左括号,则用递归调用parseExpr()处理括号内的内容即可

多项式计算类Polynominal

  • 笔者用Hashmap存储多项式的每一个单项式,其中key用一个Arraylist存储xyz对应的指数,value部分存储单项式的系数
  • 其中有add/sub/mul/pow方法进行计算和合并同类项,最后通过重写toString方法输出化简后的表达式

因子类接口(Factor)

详见training的架构

  • 将Expr,Power,Constant,Variable均视为因子

    笔者的一些bug

  • BigInteger的相关问题:慎用.valueof,第一次在写parse类时出了一个愚蠢的错误

    BigInteger num = BigInteger.valueOf(Long.valueOf(lexer.peek()));//wrong
    BigInteger num = new BigInteger(lexer.peek());//correct
    

    关于new xx() 与 xx.valueof()的区别 参考https://blog.csdn.net/m0_73097650/article/details/126880577

  • 在重写toString时,写错了一个ArrayList对应的数组下标,导致y和z的指数搞错了,测试时没有测试出来

基于度量的程序结构分析

  • 代码规模分析

在这里插入图片描述

  • 方法复杂度分析

在这里插入图片描述在这里插入图片描述

参数含义:

  • ev(G) 基本复杂度是用来衡量程序非结构化程度的,非结构成分降低了程序的质量,增加了代码的维护难度,使程序难于理解。

  • v(G) 模块设计复杂度是用来衡量模块判定结构,即模块和其他模块的调用关系。软件模块设计复杂度高意味模块耦合度高,这将导致模块难于隔离、维护和复用。

  • v(G) 是用来衡量一个模块判定结构的复杂程度,数量上表现为独立路径的条数,即合理的预防错误所需测试的最少路径条数,圈复杂度大说明程序代码可能质量低且难于测试和维护,经验表明,程序的可能错误和高的圈复杂度有着很大关系。

  • 类复杂度分析
    在这里插入图片描述

优化Tips

  • 可以把x**2用x*x替换,减少长度
  • 把多项式的首个非负项放在表达式首位显示

第二次作业总结

分析

  • 本次作业在第一次作业的基础上增加了三角函数,自定义函数以及一些数据的更新和细节。因此笔者选择在第一次作业的基础上进行迭代开发,一一实现相应功能就好。

迭代开发

总UML类图

在这里插入图片描述

三角函数类Trifunc

  • 该类的属性symbol存储sin/cos,expr存储括号内的表达式
  • 重写toString()方法,将sin/cos视为单目运算符转后缀表达式输出
    • 例如:sin((x**2 + 1)) ——> x 2 * * 1 + sin

自定义函数类SelfDefineFunc

  • 该类的addFunc读入一个自定义函数表达式,并转换成Hashmap<String,ArrayList<String>> 形式存储,例如f(x,y,z) = string 存储为<f,<x,y,z,string>>
  • 该类的convert方法用于自定义函数的调用
  • 笔者对于自定义函数的处理方式是在递归下降之前先对表达式进行预处理,遇到自定义函数就转换成对应的string最后再统一递归下降转换成后缀表达式并计算

修改

  • 读取类和解析类:新增了对sin和cos的识别与解析

  • 多项式计算类:

    • 新增了sincos方法,返回一个多项式

    • 修改了存储多项式的方式,改为

      private HashMap<HashMap<String,BigInteger>,BigInteger> expression;
      //value值存储该单项式的系数,key值的Hashmap存储(x,2)-->x**2;(sin((x*x)),2) -- > sin((x*x))**2
      

笔者的一些bug

  • sin/cos内部括号的判断,如果是sin(Factor) Factor可以不加括号。因此,笔者在多项式计算类新增了**isFactor()**方法判断括号

  • 自定义函数的空白符和括号的处理,例如以下样例
    1
    f(x) = x**2
    f(sin(x) ** 0)

    带入后应该加套括号,(sin(x)**0) **2

  • cpu TLE

基于度量的程序结构分析

  • 代码规模分析

在这里插入图片描述

  • 方法复杂度分析

在这里插入图片描述
在这里插入图片描述

  • 类复杂度分析
    在这里插入图片描述

优化Tips

  • 若因子长度为1,则可以将因子**2用因子*因子来替换 (warning:三角函数中的因子*因子不要反向优化)
  • 三角函数的恒等变形:
    • sin((-x)) = -sin(x)
    • sin(0) = 0 cos(0) = 1
    • sin(x **2 ) + cos(x **2 ) = 1
    • 二倍角公式
  • //···

第三次作业总结

分析

  • 本次作业在第一次和第二次作业的基础上新增了自定义函数的嵌套使用和求导因子。经过思考之后,笔者仍旧选择在前两次作业的基础上进行迭代开发,一一实现相应的功能

迭代开发

总UML类图在这里插入图片描述

求导类Derivation

  • 该类的属性symbol存储求导因子(dx,dy,dz),expr存储待求导表达式
  • 通过重写toString开启求导之路

修改

  • 在Factor接口新增求导方法dao(String) ,对于表达式的求导,采用递归下降解析层层递进,并重写每个层次的toString
  • 对于嵌套调用自定义函数,笔者仍采用第二次作业的”预处理“方法,特判输入自定义的函数个数,用for循环进行嵌套调用(可能较为愚蠢)

笔者的一些bug

  • 在求导过程中,可能出现指数计算得0的情况,由于笔者存储多项式的方式的缘故,会产生nullpointerexception,因此,增加了一个特判
  • 求导过程中忘记转换后缀表达式,导致调用计算类计算时出现bug

基于度量的程序结构分析

  • 代码规模分析
    在这里插入图片描述

  • 方法复杂度分析

    在这里插入图片描述在这里插入图片描述
    在这里插入图片描述

  • 类复杂度分析

在这里插入图片描述

优化Tips

  • 同第二次作业

架构设计体验

在这三次作业中,我并没有经历让人痛不欲生的重构。这得益于递归下降算法的天然优势和对下次作业需求变更的合理预测

在第一次作业中,通过课上实验的抛砖引玉,我确定了采用递归下降算法来解析表达式,即使在之后的迭代中增加因子也可以处理,实现“高内聚,低耦合”。

这一单元的作业让我理解了一个良好架构的优越性,在第二次和第三次作业中我甚至只增加了200行代码就完成了相应的需求。在以后的作业中我也应着眼于设计层面,在编码时多多考虑代码的可扩展性。

bug分析

  • 三次作业强侧加互测共找出4处bug(见上文笔者的一些bug),都在前两次作业中出现。刚刚接触oo课程并不理解测试的重要性,导致前两次作业中出现了非常愚蠢的bug(强侧分数骤减 >_<),让我明白了自动化测试的重要性
  • 对于我分析别人程序bug所采用的策略
    • 我先阅读对方的代码,寻找特殊值和漏洞
    • 在第三次作业的互测中采用了讨论区中大佬的评测姬做自动化测试

心得体会

在第一单元的作业中,通过三次迭代开发,我逐步掌握了熟悉了git、代码风格检查的使用。通过不断的coding和阅读他人代码慢慢理解面向对象编程的思想。我也深刻地体会到一个良好的架构以及自动化测试的重要性。在下一单元的学习中,我要多多关注代码风格和顶层架构,多做测试!多做测试!多做测试!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

psfott

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值