zoukankan      html  css  js  c++  java
  • 面向对象第一单元博客作业

    0 引语

      经过四周的学习,面向对象第一单元也告一段落,在本单元的学习中,我们通过迭代开发的方法,完成了三次有关表达式求导的作业。每次作业都是一次挑战,从周二开始到下周二的一个轮回之中,将经历立足根基的评测,紧张刺激的互测,亡羊补牢为时不晚的bug修复三个环节。下面,我将以博客的形式对这一单元的内容加以总结和分析,以发表我的看法。

    一、程序结构分析,迭代和重构

    1.1 第一次作业

      第一次作业是基础中的基础,程序结构和功能都较为简单,每一个因子都可以简化成a*x**b的形式,多个这样的因子构成了一整个表达式。程序类和方法的命名选用了我喜欢的命名方式,我在这里先对各类的命名进行一个解释。

      (1)MainClass:主类,一切的起点也是终点,含有main方法和专门用来将字符串(其实是字符数组)进行程序化处理的chuli方法,出于某种原因,我并没有采用正则表达式进行处理(在预处理阶段使用了少量的正则表达式处理),程序的核心部分使用了字符自动机进行处理。

      (2)ChaosString:混沌之串,第一次作业“表达式”的称呼,其包含一个Arraylist,定义为Arraylist中“项”的加和。由于本次作业只包含项相加的形式,此类的构造也较为简单。这个类中包含了ChaosBurst(混沌爆发)方法(其实就是求导),返回另一个ChaosString表达式。

      (3)MoonStar:星月,之所以叫这个名字是因为“乘号”像一个星星,系数用star代表恰如其分,而指数自然就使用了比它高一级的“月亮”。这个类拥有独特的星空之语speak方法,用于直接输出一项的结果,输出的其余部分则在主类MainClass中完成。主类要干的事情太多了!

    第一次作业方法复杂度分析:

     第一次作业类复杂度分析:

     

        从复杂度分析结果来看,第一次作业我仍旧使用面向过程的思想,MainClass的负担很重,另外两个类我也只是把它当成“结构体”来使用,面向对象语言的优点无法得以发挥。尤其是主类中的chuli方法,字符自动机处理的过程很长,使用到了许多行的代码,在编写过程中曾一度超过60行触发Checkstyle警告。

    1.2 第二次作业

      第二次作业和第一次作业相比,主要增加了两个东西:三角函数sin/cos和WF(Wrong Format)的判断。每个项可以简化为k*x**a*sin(x)**b*cos(x)**c的形式,同样是多个这样的因子构成了整个表达式。由于项只能表示为因子相乘的形式,而因子最终可以简化成固定的形式,所以整体难度依然不大。

      本次作业我仍旧是通过三个类MainClass,ChaosString,Moonstar构成,此次我对Moonstar进行了重点改造。修改之处如下:

      (1)MainClass:程序的核心部分依旧使用字符自动机进行处理,为了避免方法长度过长,我重构了chuli方法,将一个较大的方法分解为多个小方法,分解标准为自动机读到每一类字符时执行对应的方法,如读取数字的read09,读取乘号的readstar等。读取加减的readass全名是read-Add&Sub-Sign,并不是你们想的那样(滑稽)

      (2)ChaosString:无太大变化,仅求导方法更改。虽然写了利用sin^2+cos^2=1的简化方法,但因为存在bug而未实装。

      (3)MoonStar:本次最大变化的类,将原来的moon分为三个:xmoon,sinmoon,cosmoon,分别记录指数,系数由于是相乘的关系,可以直接乘起来,故仍使用star一个变量进行记录。同时,重构了speak方法,由于新背景下输出方式更加复杂,这一部分花了我不少心思。

    第二次作业方法复杂度分析:

      第二次作业类复杂度分析:

      从分析结果看,这次的主类复杂度比起第一次作业好了很多(10.50↘5.86),其中处理字符串的chuli方法和输出结果的speak方法仍比较复杂。从这里看来,面向对象的方法已经初具雏形,但还未完成从面向过程到面向对象的过渡。如果想让我的程序更“面向对象”,可以使用继承思想将x,sin,cos的因子安排的更好一点,或者使用工厂模式的思想,建立一个单独的类以专门使用自动机处理字符串。总而言之,这次的作业还存在着改进空间。

    1.3 第三次作业

       第三次作业加入了套娃表达式嵌套系统,在因子中可以加入新的表达式,这对我来说可谓是一个较大的挑战。因此,我对整个程序进行了很大的改动,新增了很多各种各样的类,当然,命名依旧采用了我喜欢的方式,程序嘛,为何一定要冷冰冰的呢?采用自己喜欢的命名的话,编程的效率也会比较高哦!

      (1)MainClass:主类,这次的主类可谓是相当简洁,只有字符串的预处理和一些调试操作,就连程序的输出也不在Main中,而是随着每一个类的speak方法深入各处了,求导方法也是每个描述表达式的类都“人手一个”,程序的核心部分已经被隐匿在各个类中。宇宙的奥秘不会主动对外呈现。

      (2)Universe:宇宙,ChaosString的升级版,依旧是包含了多个项(表示项的类是Sun),语义上是由所有的项相加组成的一个表达式。含有从MainClass复制来的speak方法(这个复制就很坑,由于复制过来不认真看,直接导致了我本周作业的一个bug)

      (3)Sun日(←划)太阳,这东西其实是题目要求中的“项”,项的求导方法较为复杂,由于此次项可以由多个因子相乘得到,而这些因子无法像前两次那样化简为简单的格式化乘数形式(只有幂函数a*x**b因子可以简单合并),所以,这个Sun中就包含了一个幂函数因子MoonStar,一组三角函数因子SinStar,一组表达式因子Universe。这么看的话太阳内部可以有一个宇宙?!

      (4)MoonStar:星月,这个Moonstar去掉了sin和cos,其实相当于是一种“退化”的Moonstar,某种程度上和第一次作业的同名类更为接近。

      (5)SinStar:罪恶之星(双关,sin也有罪恶的意思),说它罪恶它是真的罪恶,造成本次作业难的一个罪魁祸首。内含一个大材小用的Sun(Sun本来是描述项的,即多个因子相乘,现在却只用来储存单个因子),以及常规的指数等。

      (6)ChaosFactory:混沌工厂,第二次实验学习工厂模式后的拙劣产物。本次作业为了便于递归嵌套,把字符自动机搬到了一个单独的类中,这就是这个类的来历。它的核心方法是Create,用于从字符串(这回没有再使用字符数组了)生成一个Universe,副方法是CreateSun,用于处理三角函数括号内的情形,生成一个因子。这两个本身是同样的东西,只是后者由于只能生成因子(不使用括号的情况下不能存在+-*),在处理Wrong Format的过程中要比前者严格。

    第三次作业方法复杂度分析:

         

     第三次作业类复杂度分析:

        可以看出,这次的程序本身有一定的难度,所以方法和类都较多,不过,方法的复杂度实际上并没有特别高,这是因为本次作业的面向对象思想已经基本体现出来,之前面向过程的一main到底思想已荡然无存。通过这三次作业,我看到了我的进步,也看到了面向对象思想的应用从无到有,从少到多的过程。不管怎么说,我三次作业都拿到了90分以上的分数,可以基本认为这几次作业是成功的。

      可喜可贺,可喜可贺。

    二、程序bug分析及改正

    2.1 第一次作业

      值得欣喜的是,第一次作业我并没有在强测中测出bug或在互测中被hack,所以也无需进行后续的bug修复工作。

      在中测以前的自行评测过程中,我发现程序主要的bug有:

      (1)由于输出格式不当导致的错误输出(如-*x**-1,0没有输出等)

      (2)由于使用int/long而非BigInteger导致的溢出问题

      (3)由于合并同类项不当导致的丢项问题

    2.2 第二次作业

      我在第二次作业中,尽管通过了强测的全部测试点,但是被hack出了两处错误(在修复环节证明其为同质bug)。

      此次被hack后,我在互测结果公开之前,就已经自行调试并发现了这个bug,原来,我在之前的版本中,错误理解了幂函数指数中有关10^5的限制,错误的选用了long作为储存指数的工具。实际上,使用long储存x的指数固然可以,但储存三角函数的指数就不太行了。除此之外,在这一部分容易出现的bug还有:

      (1)输出格式!还是输出格式的bug,这次的输出较为复杂,格式上更容易出错。

      (2)有关WF的判断。有些人的程序,该判WF的不WF,不该WF的乱WF。

      (3)化简出错。通过化简来赢取性能分固然是好的,可一切都要建立在保证程序正确的基础上,否则会得不偿失!

    2.3 第三次作业

      很遗憾,第三次的作业,我没能通过全部的强测测试点,同时在互测中被hack到6处错误。

     

      程序中越放松警惕的部分越容易出bug。    ——沃·兹基硕德

      这次作业的bug,都反映出了一个统一的现象,那就是——“0”被提到前面去了!究其原因,居然是因为……

        这里!我在前面提到,这段代码是从前面的Main复制来的,之前在只包含一个表达式的时候,遇到空表达式直接输出0无可厚非,然而,当我把speak放到Universe内部时,程序应该返回一个字符串,也就是说,此时不应该输出0,而是返回0!这个bug让我白白丢分,实属不该。

    三、让人又爱又恨的互测

    3.1 Hack技巧与思路

      互测是本次面向对象课程的新增玩法,6或7个人在一个房内代码公开,打打杀杀,相互Hack。由于30分钟“开枪冷却”的设置,Hack也成为了一门学问,它也需要有技巧和思路,下面我就来实际谈谈Hack的技巧与思路。

      (1)前2个小时至关重要

      在互测阶段,选手只能看到自己被Hack的情况,以及整个房间内所有人Hack别人的情况。而提交的冷却期为30分钟,互测数据更新的间隔为2小时。为什么说前2个小时重要呢?因为,这个时间通常提交的人不多,你可以从他们的提交信息中判断出的有用信息也较多。比如,在第一次数据更新后,有人的数据变为了3/14,这说明他在两次尝试内Hack了三次别人的代码,我们就得到了这样一个信息:房间里至少有2人的代码拥有bug,有可hack空间。如果第一次数据更新后,所有人的Hack数据全是0,甚至出现了0/21的情况,那么,你所在的这个房间可能全是dalao根本不存在bug或极少存在bug。

      (2)广撒网VS定向攻击

      每两次提交互测测试点需要间隔30分钟,前期你并没有时间来得及下载并查看房间内所有人的代码,怎么办呢?这段时间也不能浪费掉,因此,我提出了“广撒网”和“定向攻击”两种Hack方法。

      “广撒网”是一种大量的,盲目的打击,相当于一个机枪手拿着机枪对敌人的区域一顿扫射,有没有打中一概不论。在互测环节中,提交Naive(未测出任何bug)样例并不会有任何惩罚,所以大可放心提交。在这种打击方式下,你不用仔细阅读他人代码或进行调试,只需要挑选自己在互测之前精心准备的“陷阱”样例,争取此样例能够击中一个或多个人,每半小时提交一次,不计结果。这种方式,提交的样例有时无效(Sometimes Naive)是非常正常的现象,而自己的战绩可能是2/70甚至0/105。

      “定向攻击”是一种精确的,定向的打击,相当于一个狙击手在仔细侦察敌人的详细位置后,只用一发子弹瞄准敌人的弱点给予其致命一击。这种方法通常要仔细阅读别人的代码,相当耗时间,不过一旦成功就一定能Hack到人。如果采用这种方法,需要把别人的代码放到自己的机器上详细测试,像对待自己的代码一样对待它,有时候,还可能需要自动化评测等高端玩法。

      据我的经验,互测前期(前4个小时)大可使用“广撒网”战略,测试点能提交一个是一个,尽可能靠数量胜过质量,争取能随便打中什么人。互测的中后期,则需要结合房间整体情况,仔细下载别人代码进行分析,有针对性的进行Hack。

      (3)2-8定律推论

      80%的bug集中在20%的模块中。    ——第四周讨论题

      我使用的是另一种2-8定律,80%的bug集中在20%的人身上,而20%的bug在80%的人身上。    ——沃·兹基硕德

      2-8定律是一种很神奇的东西,几乎所有的领域都能应用到它,有时候也被称为3-7定律。在第四周的讨论中,老师和同学针对2-8定律展开了不错的讨论,而这个定律也同样适用于互测环节。大部分人的代码其实是没有bug的(这里只针对于A Room),对它们进行Hack就算是再长的时间也难以成功,相反,有些人的代码却有一个又一个的bug,拿各种样例一测一个准。所以,互测讲究2-8定律,即当你在一个人的代码上发现一个bug时,在接下来的本地测试中额外关注这个人的代码,很有可能还存在其它的bug。尽管这其中有些bug是同质bug,但若发现非同质bug或者那人不修复或无法修复,所以有时候你可以交同质bug赌一把,就相当于实实在在的赚到了分数。

      (4)人不犯我,我不犯人?

      中国有句古话,人不犯我,我不犯人。互测中你对面的也是和你一样实实在在的人类,而不是机器什么的,所以互测屋中的情景就显得格外真实:有些屋子,整个屋子也没hack出几个错误,甚至有人连提交都没提交过几次;有些屋子,大家简直杀疯了,几乎每个人都hack到了人,但也都遍体鳞伤。我这次互测,在第三次作业互测屋中就体会到了这种“杀疯了”的感觉。Saber光速7hack,Caster紧随其后,我也没能幸免于难。

      但是,“人不犯我,我不犯人”的下一句是:人若犯我,我必犯人。所以,最后我也加入了他们,在最后2小时内拿下三杀,甚至取代Saber成为了屋子内hack次数最多的人,以下为本房间的最终结果。其实,适度活跃对找bug和修复能力都有帮助,太佛系了反而不好。

    3.2 实际Hack情况

      在第一次作业中,我因被Hack扣分0分(0个错误),通过Hack别人得到约2分,赚了2分

      发现的bug是:-1*x**-1的格式输出错误;多项式项数过多爆栈

      在第二次作业中,我因被Hack扣分1分(2个错误),通过Hack别人得到0.5分,亏了0.5分

      发现的bug是:x**10000会被判断为WF

      在第三次作业中,我因被Hack扣分1分(6个错误),通过Hack别人得到约2.5分,赚了1.5分

      发现的bug是:大量括号嵌套出现超时;大量括号嵌套又出现超时;求导计算错误;括号中为0时不输出

      总而言之,我在互测中还是较为活跃的,在今后的互测中,我也会继续努力。藏好自己(指不让自己的程序有bug),做好清理(指突突突)。

    四、总结与感悟

       终于,说了这么多,要到了总结的时候了。通过三次作业的对比,我发现这三次作业我的面向对象思想逐渐深刻,三次作业从第一次超复杂的main到第三次的各类协调及初级工厂模式的应用,“面向对象要素”不断增加,“面向过程要素”不断减少。在互测中,我一直都表现得很积极,认真阅读他人代码并寻找bug。如果说还有什么缺点的话,第一,我对继承和接口的使用还不够有觉悟,第三次作业中,其实已经完全可以用继承和接口来解决问题,但我反映到作业上的结果是并没有使用。第二,我对表达式化简这类追求完美的操作做得并不好。注意到我在博文中谈论“化简”的部分少之又少,这其实是因为我除了第一次作业拿到了满分性能分外,后两次的化简都没有化出我想要的结果,在“性能”和“正确”上我亦然选择了后者,一心求稳。其实,在今后的作业中,我可以在保证正确的前提下比之前多追求一些性能分,争取拿到更高的分数。最后,第一单元的三次作业已经过去,崭新的篇章即将开启,让我们满怀着对课程的热情,投入到新的单元中去吧!

  • 相关阅读:
    一般 武胜
    Export/Import CSV files with MySQL 武胜
    关于Python中的for循环控制语句
    Linux下libxml2的使用
    Foxit PDF Reader能有效升级日文包
    Dependency introduction
    eclipse 插件更新站点
    TypeError: 'module' object is not callable 原因分析
    ubuntu11.10 安装reportlab出现“Python.h: 没有那个文件或目录”
    Windows 7 下如何调整网卡的优先级
  • 原文地址:https://www.cnblogs.com/yuanshaohan/p/OO_BlogHomeWork_Unit1.html
Copyright © 2011-2022 走看看