[BUAA OO Unit 1]第一单元总结 —— 递归下降思想+后缀表达式的架构思路

Part0 总体思路

0.1 题目简介

三次作业总体上都是在解决一个问题:读入一个单变量(即x)表达式,输出恒等变形展开所有括号后的表达式。只是在每一次迭代中新增了一些算子如求导、exp指数函数、自定义函数等。

0.2 题目分析

总体上我的处理思路分为两步:
1.将表达式化简成后缀表达式字符串。 eg:(x+1)*(x+2) -> x 1 + x 2 + *
2.将后缀字符串作为参数传入计算类中进行计算。
即 从左到右遍历,读到操作数就压栈,读到运算符就从栈顶取出对应的元素计算出结果并压入栈中
(当然,这里的运算符不仅可以是 + - * ^等,也可以把exp视为单目运算符
例如:exp(x^2) -> x 2 ^ exp 这样就可以解决第二次作业的新增内容
dx即求导算子也是同理)
首先,题目最基本的要求是要把不必要的括号全部展开,所以如何展开括号呢?课题组的第一单元的训练题中提供了两种思路:正则匹配和递归下降,考虑到后续有多重括号嵌套,递归下降显然是更优选。
关于递归下降的理解,建议看oo公众号中的文章并结合第一单元训练提供的代码,还是很容易理解的。

0.3 预处理

我们发现表达式可以有无限多的空白字符、冗余的正负号,难道要用lexer一个一个读过去吗?这样是麻烦且容易出bug的,所以可以对传入的表达式先进行预处理,再进行展开,以下是我的预处理思路:
1.删除全部空白字符
2.如果表达式第一个字符是符号位,补一个0
3.除去冗余的+、-号。 eg:+ -1 -> -1
4.^+ -> ^
5.* + -> *
6. -x -> -1*x
具体怎么预处理还要看你的后缀表达式是怎么去算的,这里只提供部分思路,可以根据自己的处理思路选择对应的预处理方法。

Part1 HW-1

1.1 题目要求

读入一个包含加、减、乘、乘方以及括号(其中括号的深度至多为 1 层)的单变量表达式,输出恒等变形展开所有括号后的表达式。

1.2 整体架构

1.2.1 递归下降求后缀表达式

111
这里为了处理 表达式因子的幂次方 问题,在expr-term-factor基础上只能加了power,形成了expr-term-power-factor的下降层次:
expr:若干个term 相加减
term:若干个power相乘
power:一个factor的n次方
factor:表达式因子、常量因子或变量因子。这里factor可能的情况很多,所以采用接口实现

1.3.1 toString方法

这里我的目的是要变成后缀式,因此重写每个类的toString方法,使其变为后缀表达式,上层类调用下层类的toString从而实现完整后缀表达式的展开

1.3.2 expr的处理

我们发现expr项与项之间有+/-号连接,这里我使用了两个arraylist,一个存项,一个存符号:

private final ArrayList<String> ops;//符号
private final ArrayList<Term> terms;//各个项

1.2.2 后缀表达式的计算

这里用到2个类来处理:分别是:
1.Oper类:负责对后缀表达式进行遍历计算
2.Poly类:规范类,HW-1中我们可以很容易的得出:标准答案的每一项一定长成这样子"a*x^b",因此用poly类(即多项式类)来规范计算过程
Poly中实现 add、sub、mul、pow方法,在oper中即可运用这些方法进行计算。

1.3 复杂度分析

在这里插入图片描述
这里我的架构中poly类和oper类复杂度最高,原因是poly内聚了四种计算方法,oper内的遍历方法复杂度较高,这也是比较合理的。
当然,oper中当个方法复杂度过高我认为是可以优化的,可以将其中一些操作提取出来作为内部函数,这样会降低单个方法复杂度。

1.4 bug及hack分析

1.bug:本次有一个bug,主要还是预处理的问题,没把-x -> -1*x ,导致遇到 -x ^2,会把-x当作整体,错误输出 x ^ 2 的答案
2.hack:用python写了一个数据生成器加自动评测机,每个同学的代码跑个几千次,也是找到了一些bug,错误主要也是预处理部分的问题。
Ps:自动评测部分建议参考往届宁然学长的博客和b站视频,讲的很详细。

Part2 HW-2

2.1 新增要求

1.本次作业支持嵌套多层括号。
2.本次作业新增指数函数因子,指数函数括号内部包含任意因子。
3.本次作业新增自定义函数因子,但自定义函数的函数表达式中不会调用其他自定义函数。

2.2 要求分析

1.多层括号如果使用的是递归下降可以直接跳过,正则匹配我也不会
2.增加exp类存放指数函数因子
3.对自定义函数的处理思路:
(1)增加Function类,在parser中添加hashmap属性来管理自定义函数我这里采用边递归下降边展开自定义函数的思路。

//在paserfactor中新增:
if(f||g||h){
}
//parser
private final Map<String, Function> funcs = new HashMap<>();//函数

(2)Function类中写方法进行形参和实参的替换,将实参作为参数传入,return替换后的经过parser的表达式即可。

2.3 复杂度分析

在这里插入图片描述
这里的复杂度主要还是集中在Poly类中,体现了"高内聚,低耦合"的思路,还是比较合理的

2.4 bug及hack分析

1.bug:强测错了一个,原因是用int存储指数,但是可能会超过int范围,改为用BigInteger存储即可
2.hack:使用cxc同学的公测平台进行debug,没有发现别人的bug。

Part3 HW-3

3.1 新增要求

1.本次作业支持求导操作,新增求导算子 。求导因子可以出现在很多位置,包括函数调用实参,指数函数内部等,注意考虑周全。
(为了限制难度,在输入中,求导算子不会在自定义函数中出现)
2.本次作业函数表达式中支持调用其他“已定义的”函数(保证不会出现递归调用)

3.2 要求分析

  1. 对于求导因子:依然将之视为一个单目运算符,其作用是对Poly规范类求导,return一个新的Poly类,在Poly类中添加der计算方法即可。
//Poly
public static Poly der(Poly a) throws CloneNotSupportedException {
	//your code 实现求导
	return Poly mypoly;
}
  1. 因为HW-2中我的思路是边递归边展开,所以在展开的表达式中如果还有自定义函数依然会继续展开,所以不需要做任何修改。

3.3 复杂度分析

在这里插入图片描述
本次作业仅仅在Poly中新增一个函数,代码修改较少,也说明了这个后缀表达式处理方法具有良好的延展性,依然遵循"高内聚,低耦合"的理念,比较合理

3.4 bug及hack

bug:这次的bug是预处理部分忘记把自定义函数内例如 f(+01,+01)中的两个+号去掉就直接进入parser了,而我的paser默认第一个一定是term,出现bug
hack:采用自己构造数据+使用评测机大量评测,未发现bug

Part4 心得体会与未来方向

4.1 心得体会

  1. 我的数据测试和debug做的不好,很多时候不能找到别人的bug,构造的一些样例也存在强度过低的问题,导致没能找出自己或别人的bug。
  2. 我自己的代码风格较差,有的属性加了get、set方法,有的没有,但我自己是不知道什么时候必须加,什么时候不能加的,分析问题、分析需求的能力还是有待提高
  3. 我写作业的时候存在“拖延”的情况,有时候会把作业留到周四周五做,这一点需要改一下,最好周一晚上就开始分析问题并思考自己的架构,要做到心里有数。

4.2 未来方向

  1. 尽早进行作业架构,绝不拖延
  2. 注重代码质量,学习使用单例模式,对于需要调用函数内部属性的类统一设置get、set方法,其他不设置,统一代码标准
  3. 为数据测试、debug留出至少一天的时间,进行充分的覆盖性测试,同时尝试用python编写一些较难的数据生成器,例如HW-2 HW-3的数据生成比HW-1复杂很多,要多尝试去写。
  • 15
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值