第一单元总结博客
概述
》》第一单元是关于函数求导的迭代开发,三次作业中,第一次通过中测,第二次通过弱测,第三次没有提交。通过的两次作业中,第一次在完成bug修复后通过强测,第二次在bug修复中未能通过强测。作业完成度很低,需给予重视。作业的主要思想是有限状态机和栈。由于完成第三次作业,在度量分析过程中,仅针对对第一、二次作业进行。
度量分析
第一次作业
》》第一次作业十分简单,设计一个自动机就能满足需求,作业自动机的设计思路已经在后续作业中更改,将在后续设置一个专门的小结详述。
第一次作业反映出的问题
》》第一次作业问题问题大部分是由于自己安于现状所导致的,在以后的作业中会通过同学监督和自我批评逐渐改正。
@首先是代码风格的问题,在完成pre的时候对代码风格不太在意,第一次作业中的main方法行数达104行,远超60行。这一情况在第二次作业中有所改善。
@其次是未将表达式进行简化,例如将幂函数符号转化为^,这一想法仅在脑海中掠过,并没有去实现,在第二次作业是也是如此,例如将sin(x)和cos(x)转化为s和c等,这一问题知道第二次作业的debug环节才予以解决。
@然后是在hack的过程中,由于没有自动测试,以及从下载src文件运行需要重新创建IDEA项目感觉很麻烦,大部分的测试数据都是自刀。
@最后是有关新知识。尽管在网络上对正则表达式以及各种list进行了了解,但是在写作业过程中并未充分利用,或者说不知道怎么使用才能比自己目前的代码更好。在完成作业的过程中存在重复造轮子的隐患。
第一次作业代码分析
》》第一次作业代码实现了MainClass,Object和rnum三个类,MainClass用于解析字符串,Object用于存储表达式中的项,rnum用于处理函数返回值参数有多个的情况
@优点是每个函数和类的功能都很容易理解,并且实现比较简单。
@缺点是功能大量聚集在MainClass中,造成MainClass相对复杂。
》》总体结构如下图所示

》》第一次作业度量分析


》》方法长度中,由于main中的自动状态机主体使用了switch同时还需要进行状态的转变,控制语句数量和方法长度都很大。
》》第一次作业bug分析
@第一次作业状态机设计时忽略了x * 后面出现x的情况,造成mark与实际不符,甚至x * x都无法通过。在对自动机进行的补全以后,修复了这个bug
@如问题分析中所述,在自己构造测试样例时采取了自刀的方式,若其他人和自己出现相同bug才能够成功hack
第二次作业
》》第二次作业增加了sin,cos这些无感痛痒的式子,但是出现了括号的嵌套是问题变得十分复杂。本次代码并未在第一次代码上进行扩展,而是对自动机思路进行整理以后,利用类似栈的思想对代码进行了重构。相比于第一次作业,将各种功能分散导致,函数长度降低,数量激增。
第二次作业反映出的问题
》》第二次作业问题问题,第二次作业难度增大以及自身基础不稳固导致了以下问题。
@首先是一个“历史遗留问题”,对递归的无法理解从学习代码开始就一直存在,虽然能勉强看懂递归代码,但是自己却无法实现,导致我只能放弃许多与递归有关的简单办法。
@其次是关于自动机,括号的引入导致自动机变得十分复杂,在设计自动机时由于考虑不全导致各种bug
@最后是关于代码风格的问题,在一开始main函数中方法太多,导致长度超过了500行,为了保持良好的代码风格,只能新建一个类将一部分方法移植到那一个类中。虽然解决了这个问题,但是没有在写代码的时候就把方法写入另外一个类,降低了效率。
第一次作业代码分析
》》第二次作业代码实现了MainClass,Term、Mehod,Newist和Putin五个类,MainClass用于解析字符串,Term用于存储表达式中的项,Putin相当于全局变量,用于在函数中修改传入参数的属性,Method用来移植MainClass中过多的方法以保证代码风格。Newlist是用来实现栈。由于方法过多,画类图的时候仅列出部分重要方法。
@优点是在调试时能够看到函数解析字符串的具体过程。例如项是如何进栈出栈的。
@缺点是各个函数联系紧密,修复bug时牵一发而动全身。有时甚至忘记改动了哪里。
》》总体结构如下图所示

》》第二次作业度量分析




》》如图所示,即便忽略get和set方法,函数的数量也是肉眼可见的多。这并不是一件好事,越多越难以维护。与第一次作业相比,平均方法长度有所降低,但是平均class长度大大增加。
》》第一次作业bug分析
@第二次作业未能进入互测屋,在bug修复环节中,对栈中元素的合并出现了难以攻克的问题,由于在解析过程中将数据全部存入栈中,知道最后才进行整合,导致了许多意料之外的错误。甚至在修复过程中出现了对立修复(在修复未通过数据后,其他之前已通过数据全部出错)、区域重构的现象。对这一现象,认为产生的原因是类比数据结构表达式求值问题时忽略的部分细节(由于记性不好忘记了部分细节)。有时候1 + x * (x + x ^ 2)出现第一个x与1先相加的情况,在其他版本中,存在)后接 * 就会导致结果出错的问题。
》》重构经历总结
@关于重构,在思路上并没有很多改变,感觉完全可以进行增量开发,但是由于作业一为了修复bug导致代码风格留下的大量问题,修改工程量也很大,最终决定进行重构。完成重构的时候还是蛮有成就感的,至少是在提交之前。
有限自动机思路
//本次作业保证输入数据全部为合法(格式正确)的表达式,不需要进行格式检查!!!
//输入可能是 9,x,+,,c,s,(,),^
//状态0:
//输入(进入状态0,栈顶加一
//+:状态一
//输入9,继续获取数字并进入状态2
//输入x进入状态3
//输入c进入状态4
//输入s进入状态5
//状态1:只有+
//输入+仍然是状态1
//输入9,继续获取数字并进入状态2
//输入x进入状态3
//输入c进入状态4
//输入s进入状态5
//输入(进入状态6
//状态2:带符号数字
//输入+进入状态1,获得项
//如果输入号进入状态7
//输入),存入,执行合并
//状态3:x
//输入进入状态8
//输入+,指数++,获得项,进入状态1
//输入^获取数字进入状态2
//输入),存入,执行合并
//状态4:c
//输入进入状态9
//输入+,指数++,获得项,进入状态1
//输入^获取数字进入状态2
//输入),存入,执行合并
//状态5:s
//输入进入状态10
//输入+,指数++,获得项,进入状态1
//输入^获取数字进入状态2
//输入),存入,执行合并,嵌套层数-1,进入状态0
//状态7:数字
//输入+,9,继续获取数字乘normal,进入状态2
//输入x进入状态3
//输入c进入状态4
//输入s进入状态5
//输入(进入状态0,存入栈顶+1,将栈顶加2符号变为
//状态8:x
//输入+,9,继续获取数字乘normal,进入状态2
//输入x进入状态3
//输入c进入状态4
//输入s进入状态5
//输入(进入状态0,存入栈顶+1,将栈顶加2符号变为*
//状态9:c*
//输入+,9,继续获取数字乘normal,进入状态2
//输入x进入状态3
//输入c进入状态4
//输入s进入状态5
//输入(进入状态0,存入栈顶+1,将栈顶加2符号变为*
//状态10:s*
//输入+,9,继续获取数字乘normal,进入状态2
//输入x进入状态3
//输入c进入状态4
//输入s进入状态5
//输入(进入状态0,存入栈顶+1,将栈顶加2符号变为*
//状态11:)
//输入+,状态1
//输入*,状态12
//状态12
//输入9,状态14
//输入+,状态13
//输入xcs;151617
//输入(
@有限状态机思路大致如上所示,在实现过程中,少量细节可能出现部分不同。
心得体会
》》在学习过程中,大家都在强调要形成面向对象的思想,但是在我实际使用java的过程中,或许是由于IDEA的缘故,只是感觉比面向对象的c语言简单很多。例如,在学习c语言时,使用的是devc,只有自动补全括号的功能,我几乎没有自觉去了解和使用各种库函数,但是在写java(IDEA)的时候,会弹出各种函数,让我主动的去了解这些函数的功能以及它们如何让我的代码更加简洁,更加功能强大。但是你要问我面向对象究竟是什么,我也回答不上来。这一单元,我不敢说自己尽力了,希望在以后的单元中,我能将面向对象活学活用,在以后的作业中,说出自己尽力了。
浙公网安备 33010602011771号