第一单元博客

文章详细回顾了北航一门课程中关于面向对象设计的第一次到第三次作业,涉及表达式括号展开、多项式计算和求导。作者分析了程序结构,包括方法规模、类规模和类图,并讨论了架构设计、性能优化以及遇到的bug。在每次作业中,作者都发现了自己程序的不足,并提出了改进策略。此外,作者还反思了测试方法和未来作业的规划,强调了优化和自动化测试的重要性。
摘要由CSDN通过智能技术生成

北航面向对象与设计第一单元

前言

第一单元的主题为表达式括号展开,主要学习目标是熟悉面向对象思想,学会用类来管理对象。本单元需要一定前置知识,比如git的使用,Java 基础语法与基本容器的使用以及正则表达式、递归下降或其他解析方法。本单元三次作业分别为多项式展开,含有三角函数和自定义函数的多项式展开,含有求导的多项式展开,本人的代码量从500到800到1000层层递进。我将对第一单元进行分析和总结。

第一次作业

基于度量来分析自己的程序结构

方法规模
在这里插入图片描述

可以看到解析的几个方法代码复杂度都比较高。Lexer类的Lexer.next()使用if-else语句对语法单元进行解析,结构比较冗杂。Mono类的toString生成字符串时,需要考虑多种情况用if-else语句进行特判。Poly类的simplify用两层for循环,代码复杂度较高。
类规模
在这里插入图片描述

只有Parser类代码比较复杂
类图
在这里插入图片描述

该类图仅展示重要方法,具体分析见架构设计

架构设计体验

第一次作业的任务是对包含变量和常数的表达式进行展开,暂时不用实现表达式的嵌套。
我将本次任务分为三个子任务,首先是表达式模型的构建,其次是字符串的解析(我采用递归下降的方法),最后是表达式的计算。
第一,表达式模型构架
我主要使用的容器是ArrayList,ExprTerm组成,TermFactor组成,那么Expr中则定义ArrayList<Term> terms的数据,Term中则定义ArrayList<Factor> factors的数据。而Factor定义为接口,连接ExprFactorNumFactorPowerFactor三个因子类,ExprFactor需要的数据为表达式Expr expr和指数int exponentNumFactor需要的数据为一个常数BigInteger numPowerFactor需要的数据为变量String power和指数int exponent。至此表达式类的数据构建完成。
第二,字符串的解析
我采用的是训练中advance中递归下降的方法,使用Parser和Lexer两个类。Lexer将表达式分为基本语法单元,Parser根据Lexer分解的语法单元递归生成表达式项和因子,具体实现为从parseExprparseTermparseFactorparseFactor分为三个函数parseExprFactorparseNumFactorparsePowerFactor
第三,表达式的展开
我主要到三种Factor都可以转换成形如d * x^a * y^b * z^c 的形式,将该形式定义为一个单项式类Mono,其需要的数据为系数BigInteger coe,x的指数int xexp,y的指数int yexp,z的指数int zexp。表达式最终展开的形式为多个单项式相加,再定义一个多项式类Poly,其需要的数据为多个Mono,使用ArrayList<Mono> monoList,其实现的方法主要有多项式加addPoly(),多项式乘mulPoly(),多项式乘方powerPoly(),将多项式转变为输出的字符串toString()
所有表达式类都需要加入将类的内容转换成Poly的函数toPoly(),这样,我们就可以通过在每个类中定义的toPoly()方法自底向上地得到表达式的多项式形式。最后,我们再通过Poly中的toString()方法就可以获得最终展开后的结果。
性能优化
表达式的优化主要实现方法为合并同类项simplify(),具体实现为如果两个单项式xyz的指数都相等,则将两个单项式合并为一个,指数不变,系数相加,通过两层for循环实现。
细节优化:Mono的系数为1或0或-1,xyz指数为0或1,都可进行相应简化。

分析自己程序的bug

字符串解析
在parser中出现少读语法单元的情况,经过构造数据单步调试容易发现,但更好的办法是思考是更加缜密。
符号的解析出现问题。一开始在Term类中定义一个数据int sign,发现不易实现,后来实现办法是如果解析到负号,则在Term中加入一个数据为-1的NumFactor
空格和制表符解析出现问题。一开始通过文法进行解析,容易出现问题。后来与同学交流中将办法更改为对输入的字符串进行预处理,清除空格和制表符。
0的判断
在第二次作业的强测和互测中发现该问题,原因是一开始Mono为0时,toString后为0,多项式只有一个单项式且该单项式为0则输出零,后来更改为Mono为0时,toString后为空,多项式为空则输出0。

分析自己发现别人程序bug所采用的策略

本次作业未进入互测

心得体会

在第一次作业中,由于没有上oo先导课,虽然暑假自学过Java语法和面向对象基本知识,但我写过代码量较少,对Java语法和容器使用不熟悉以及对递归下降解析不是很理解,导致第一次作业进度较慢,在前三天对整个架构比较迷茫,不知从何开始入手,一直都在处于搜索相关资料和思考的环节,到周五通过阅读学长的博客我初步完成架构,正式开始写代码,周六晚上才把基本构架写完,但输出基本错误。周日早上进入debug阶段,发现解析环节出现了不少错误,最终时间不够没有通过公测。未通过公测后看着身旁同学都进入互测,我一度十分气馁,担心自己会跟不上进度。在进入bug环节后,我花了约半天时间通过了所有样例,又花了半天时间通过强测中的所有点。然而,缺少互测环节给我第一次作业也隐藏了不少bug,在第二次作业中出现错误。同时,表达式化简部分我也只实现了同类项合并,输出结果比较冗长,出现类似+1*x的结构

第二次作业

基于度量来分析自己的程序结构

在这里插入图片描述
在这里插入图片描述
复杂度问题与第一次作业类似
在这里插入图片描述

架构设计体验

第二次作业相对于第一次作业增加了三个功能,第一是多层括号嵌套(由于第一次解析采用递归下降,多层括号嵌套已经实现),第二是三角函数,第三是自定义函数。
首先在解析部分增加parseSinCosFactorparseSelfDefineFactor
三角函数
增加SinFactor和CosFactor两个类(其实可以合并为一个类更为简洁,因为两个类代码重复度较高,但是由于时间不足就没有重构),其数据为三角函数里的因子Factor factor和指数int exp
自定义函数
新增工具类Definer,存储数据为HashMap<String, Expr> funcMap(函数名->函数定义式)和HashMap<String, ArrayList<String>> paraMap(函数名->形参(x,y,z))
然后在所有表达式类中增加方法replace(ArrayList<String> formParas, ArrayList<Factor> actualParas),参数分别为形参和实参。最底层为幂函数的替换,具体实现为若变量在形参中,则返回表达式因子,表达式为对应实参,指数即为幂函数的指数。常数因子不需要替换。然后自低向上得到替换后的表达式。
表达式展开
相对于第一次作业,单项式的基本结构变为d * x^a * y^b * z^c * sin(Factor) * cos(Factor), 增加多个三角函数相乘,Mono增加数据ArrayList<SinFactor> sinFactorsArrayList<CosFactor> cosFactors,将Mono中的三角函数转变为字符串需要将三角函数里的Factor转变为字符串,因此在所有Factor类中增加方法toString
优化
构想三角函数的合并需要在所有Factor新增equal函数。但由于赶os的ddl没有时间实现三角函数的合并同类项,只完善了第一次的合并同类项(如果没有三角函数则按照第一次作业方法合并),导致最后性能分几乎都是零分。

分析自己程序的bug

输出字符串不符合文法
三角函数括号里应该为Factor,若里面有表达式因子展开后为表达式不符合文法,修改办法为三角函数toString中无脑再加一层括号
解析
解析时仍出现类似第一次作业的问题

分析自己发现别人程序bug所采用的策略

三角函数
三角函数里的因子的所有情况
自定义函数
自定义函数形参顺序更改
调用自定义函数实参为各种因子的情况
因子嵌套
三角函数,表达式因子,自定义函数互相嵌套

反思

本周任务量较大,时间也比较紧凑。我在周三下午才完成第一次作业,然后花了约一天时间实现的三角函数并进行了相应的测试。然后我在自定义函数的构思上花了较多时间,在衡量字符串层面和表达式类层面的替换,我先尝试字符串层面的替换,在实现中发现非常容易产生bug,后来在研讨课后收获不少,更改为在表达式类层次进行实参对形参的替换,几乎没有产生bug,最终在周五晚上成功通过公测。从第一次作业没通过公测,到第二次作业在两天半左右的时间内完成,我对递归下降的理解不断深入,对面向对象的思维也有了更多感悟。

第三次作业

基于度量来分析自己的程序结构

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

复杂度问题与前两次作业类似
在这里插入图片描述

架构设计体验

第三次作业相对第二次新增支持调用其他“已定义的”函数(在第二次作业中递归下降已经实现)和求导算子。
求导
在所有表达式类中新增求导方法,自低向上实现求导即可。我的求导函数均返回表达式因子,在后续debug过程中有多层嵌套,不方便进行调试,在课上实验中,认识到所有因子求导后都可以为项,项求导后为表达式,但由于重构较为复杂便没有更改实现方法。
项的求导需要利用乘法法则实现。

分析自己程序的bug

三角函数求导错误
sin(Factor)^exp 的求导的结果为der(Factor)*cos(Factor)*sin(Factor)^exp-1

分析自己发现别人程序bug所采用的策略

各种简单求导
函数定义时调用已经定义函数
求导算子里的表达式存在嵌套的情况
表达式为多项相乘
表达式嵌套表达式因子,自定义函数因子和三角函数因子

反思

本次作业新增功能较少,只需完成求导的递归下降即可,只增加约100行代码,约一天左右时间便成功实现。在本次作业中,我更加熟练掌握递归下降。

单元总结

架构在第一单元的作业中,我的架构都实现得比较好,在迭代中没有重构,同时,在上一次作业中也实现部分下次作业的功能,因此第二三次作业都完成得比较快。
进步从开学对git和idea以及Java的不熟悉,第一次作业完成的煎熬,到第二三次比较顺利的完成,逐渐建立了完成面向对象作业的信心,逐渐掌握面向对象编程的方法。
优化在第一二次作业中由于时间比较仓促几乎没有实现优化,第三次作业中由于三角函数合并的困难以及害怕产生新的bug对优化望而却步,导致第一单元的性能分非常低。在第二单元的作业中应该尽早实现优化。
测试第一单元的测试我采用的时手动构造测试数据,覆盖性非常差,互测时找bug也非常困难且低效,在未来的单元中我应该学会自动化测试,构造比较全面的数据。
展望在未来的单元中,尽早开始写作业,与同学多讨论架构,学习他人优秀的实现方法,留出充足的时间进行优化和测试,不能公测过了就开始摆烂。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值