2024BUAA_OO_Unit1总结

题目背景介绍

  • 本次作业中需要完成的任务为:读入一系列自定义函数的定义以及一个包含幂函数、指数函数、自定义函数调用、求导算子的表达式,输出恒等变形展开所有括号后的表达式。
    在本次作业中,展开所有括号的定义是:对原输入表达式 E做恒等变形,得到新表达式 E′。其中,E′中不再含有自定义函数,不再含有求导算子,且只包含必要的括号

设定的形式化表述

  • 表达式 → 空白项 [加减 空白项] 项 空白项 | 表达式 加减 空白项 项 空白项
  • 项 → [加减 空白项] 因子 | 项 空白项 * 空白项 因子
  • 因子 → 变量因子 | 常数因子 | 表达式因子|求导因子
    • 变量因子 → 幂函数 | 指数函数 | 自定义函数调用
    • 常数因子 → 带符号的整数
    • 表达式因子 → ‘(’ 表达式 ‘)’ [空白项 指数]
  • 幂函数 → 自变量 [空白项 指数]
  • 自变量 → ‘x’
  • 指数函数 → ‘exp’ 空白项 ‘(’ 空白项 因子 空白项 ‘)’ [空白项 指数]
  • 指数 → ‘^’ 空白项 [‘+’] 允许前导零的整数 (注:指数一定不是负数)
  • 带符号的整数 → [加减] 允许前导零的整数
  • 允许前导零的整数 →→ (‘0’|‘1’|‘2’|…|‘9’){‘0’|‘1’|‘2’|…|‘9’}
  • 空白项 → {空白字符}
  • 空白字符 → (空格) | \t
  • 加减 → ‘+’ | ‘-’

迭代

本单元共经历三次迭代

  1. 处理基本表达式,只包含单层括号展开和加减乘除以及指数
  2. 处理多层嵌套括号,引入自定义函数和指数函数
  3. 处理自定义函数调用,引入求导因子

个人架构

最终架构如下图所示,相关类以及函数的功能已经在图上做出阐释
在这里插入图片描述

下面对各个类的功能以及运行流程做出具体说明

类名解释说明
MainClass程序入口,负责输入输出和调用
Initial负责初始化,删除空格/合并正负号/调用Diyfunc
Diyfunc处理自定义函数,添加定义/传参
Lexer输入流,分析字符
Parser解析器,负责解析表达式,转为Expr
Unit储存基本单元,内容为x的指数和exp的指数
Basic储存化简后表达式,多项式功能
Simplification用于计算和化简,调用计算器Calculation
Calculation计算器,用于具体实现各种运算
Var
Num
ExpFactor
Power
Term
Expr
储存表达式各个层次,实现递归下降,均实现Factor接口,处理求导因子

复杂度分析

最后发现有这几个方法的复杂度爆红,其中Initial.beginDiyFunc.passParameterCalculation.mul最为严重
在这里插入图片描述

  • Initial.begin方法复杂度高的原因是处理各种传入的字符时分支过多,在每个字符下又有各自的分支,导致认知复杂度过高,应该将其分化处理
  • Diyfunc.passParameter由于我将参数传递放在初始化部分,只能对字符串进行处理,因而出现了许多特判,另外还有递归调用的情况,提高了复杂度
  • Calculation.mul的复杂度高主要原因是在UnitBasic中出现了相互包含关系,从而使得代码的可读性不高,而且存在多重循环用于处理各个项的乘法

这些类也是由于上述方法的复杂度过高而导致爆红
在这里插入图片描述

个人迭代历程

我想着重说一下第二次到第三次迭代,由于第一次迭代我在计算时将exp的指数存为String类型,假如计算出的exp指数分别为x+x^2x^2+x,这很显然是相等的两个项,但是在字符串层面他们并不相等,故此在第三次作业中做出了以下改变

  • 新增Unit基本单元,不再采用HashMap<>存储,包含powForXBasic分别用于存储自变量x的指数和exp函数的表达式
  • 重写equals方法,用于判断不同unit是否相等,方便合并同类项
  • 重写HashCode方法,用于多项式检索相应的Unit基本单元
  • 调整相应计算方法,适应新的迭代

bug分析

在第三次作业时出现了一个很抽象的bug,由于出现函数可以相互调用的功能,故我在解析函数定义时会检测并调用传参方法消除定义中的其他函数,如下代码

public void addFunc(String str) {
     	 ...
        if (matcher1.find()) {
            ...
            for (int i = 0; i < s.length();i++) {
                if (definations.containsKey(String.valueOf(s.charAt(i)))) {//判断是否有其他自定义函数
                    def = passParameter(s); //调用传参函数
                }
            }
            ...
        }
        definations.put(funcName,varAndDef);
    }

但是我忽略了自定义函数中会出现三个形参xyz,若将传参放在这一步则会出现重复替换的问题

2
f(x,y,z) = x*exp(y)+z
g(x,y,z) = f(y,z,x) + x^2 

若出现以上情况则会得到

g(x,y,z) = x*exp(x)+x //wrong

我的处理方法是将xyz替换为abc,以此来消除歧义

小tips:有的同学对exp中的x进行了特判,但我无意中发现replaceAll中的替换是正则形式,完全可以用 replaceAll( "x(?!p)" , string) 替换,不需要再写判断

继续迭代

若有继续迭代,例如加入三角函数等,目前架构也可以很好的兼容。

  • 需要新增三角函数因子
  • Unit中添加三角函数的参数basicForTri以及powForTri即可
  • 需要重写equals方法,用于判断基本项是否相等
  • 还需要增加新的化简方法,例如倍角公式、平方和为1等等

心得体会

  • 作为正课的第一个单元,难度比pre提升很多,刚开始也是无从下手,好在有学长提供的博客以及课程组的推送才得以完成
  • 在迭代中不断进步,良好的架构应该是可扩展的、可复用的,我在这一点做的远远不够,甚至有很多时候为了省时省力而精简许多要求,这对持续迭代十分不利
  • 另外我的代码层次性很差,还是存在好多高耦合的类,不太符合我们的基本原则,还需要持续改进
  • 日后在开发时应该注意分段式开发,及时测试,而不是想要一次性完成再进行测试,这样会导致功能堆积过多,出现问题不能够随时锁定相关环节
  • 感谢老师、助教、同学的各种分享和帮助,希望日后可以取得更多进步!
  • 15
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值