Fate/Beihang OO第一话——第一单元总结

概况

2020年,圣杯突然出现在了北京航空航天大学,北航数百6系学子意外被卷入圣杯战争。在这场特殊的圣杯战争中,没有刀光剑影,没有殊死搏斗,有的只是满空飞舞的头发和“嗒嗒嗒”的BGM。

闲话不提,第一单元的面向对象(没有对象)学习终也落下了帷幕,而我也成功重构三次完成了任务,而这是我对我的成果做的一个总结。我以前从未接触过JAVA语言,而学习探索一门语言往往是充满刺激、快乐与成就感,同时又充满困难、挫折和头发,这些酸甜苦辣共同构成了我这一个月以来轰轰烈烈的战斗史。

接下来便开始总结吧。

一、英灵概况——程序分析

1. 第一次作业

第一次作业很简单,此时没有对象也不清楚怎么面向对象的我直接选择了面向过程编程,类也就只有两个,其中一个还是main。

我将多项式拆开储存在Poly当中,Poly类有俩参数,指数和系数,常数则对应指数为0。

当时的我追求性能分,把重点花在了输出上,最后也成功在互测中得到了满分。

1.1 基于度量的程序结构分析

class OCavg WMC CSOA LOC
Main 9.0 9.0 13.0 59.0
Poly 3.83 23.0 20.0 83.0
Total 32.0 33.0 142.0
Average 4.57 16.0 16.5 71.0
method ev(G) iv(G) v(G) CONTROL LOC
Main.main(String[]) 3.0 9.0 9.0 9.0 57.0
Poly.add(String) 1.0 1.0 1.0 0.0 4.0
Poly.ifz() 1.0 1.0 1.0 0.0 4.0
Poly.out() 2.0 10.0 10.0 9.0 36.0
Poly.outFirst() 2.0 9.0 9.0 8.0 29.0
Poly.Poly() 1.0 1.0 1.0 0.0 2.0
Poly.Poly(String,String) 1.0 1.0 1.0 0.0 4.0
Total 11.0 32.0 32.0 26.0 136.0
Average 1.57 4.57 4.57 3.71 19.3

因为完全没有考虑到今后的扩展性,所以本次作业写得非常简单粗暴,所以也没什么复杂度可以分析的。

UML类图

1.2 应用对象创建模式

这一次作业中除了把每个项象征性地装起来之外,没有任何面向对象的成分所在。

当时天真的我对JAVA的语法尚且不太熟悉,对什么类什么方法更是半知半解。于是,我直接将表达式解析过程直接写进了main方法里,为此被checkstyle重拳出击,花了好久才把65行缩短到了60行(后来,我学会了static method,暴风哭泣)。

在解析完成之后,将对各项的求导、输出等过程分别写成了Poly类的方法。事实上,我的处理方法和以前C语言课程时面向过程并无本质区别,“对象”等同于结构体,“方法”等同于函数。这样一来,这道题倒是轻轻松松写完了,然而根本没有给后面的作业留下任何拓展空间。

当然,我觉得提前剧透没意思,所以也没怎么调查后面会有什么拓展,所以也根本没有做出尝试(说白了就是懒)。

1.3 BUG分析

本次作业一没有WF的判断,二又结构简单,所以我基本没有出现BUG,并在互测中得到了满分。

但在互测中,我因为对输出情况的疏忽被hack了一个点(出现条件极其苛刻,真的佩服)——当系数为±1时,若指数为0,则输出±1,然而在指数不为零时,我却忘记了输出正负号。在自测的时候,我改正了负号的疏忽,却忘记了正号,这导致了错误的发生。

事实上,还留下了隐患——我在处理空白字符时直接替换了\s,导致了第二次作业WA在了垂直制表符上。

1.4 心得体会

当我看到自己强测100的时候惊了,这也让我认识到强测也不是强炸天。

在次周星期三的研讨会上,听了几位大佬的分享收获很多,那时自己才对面向对象编程有了直观的认识,而这也让我在写接下来两次作业时少走了许多弯路。

2. 第二次作业

面向过程的我在看到第二次作业时便傻了眼,仿佛自己做了P0就做P5了一样,无从下手、啥都不会!更要命的是,那一周的我被其他要紧事缠得脱不开身,忙得连担心自己没时间搞OO的时间都没有。还好这时大佬救命捞了我一手——周三晚的研讨课给了我很大的启发,这也能让我在后面比较快地完成作业。我在周五晚才好歹忙完一段落,开始构思、动手,最后在28点完成了代码。

事实上,在写完之后,我发现虽然自己构思的时候似乎非常“对象”,然而却越写越“过程”,其实只完成了一点点的抽象化。我用了许多个类分别封装幂函数、三角函数、常数、项,然而大家其实都一样——本质上都是四元组。我只是避免了将所有类型搅成一团,求导、输出分一大堆情况的惨状,歪打正着地完成了一点解耦合的工作。

2.1 基于度量的程序结构分析

class OCavg WMC CSOA LOC
Cos 1.25 5.0 40.0 24.0
Digit 1.45 29.0 36.0 121.0
Item 1.5 6.0 41.0 57.0
Main 3.2 16.0 17.0 102.0
Mi 1.4 7.0 41.0 31.0
Poly 1.5 12.0 48.0 56.0
Sin 1.25 5.0 40.0 23.0
Total 80.0 263.0 414.0
Average 1.6 11.43 37.57 59.14
method ev(G) iv(G) v(G) CONTROL LOC
Cos.Cos() 1.0 1.0 1.0 0.0 2.0
Cos.Cos(BigInteger) 1.0 1.0 1.0 0.0 4.0
Cos.Cos(String) 1.0 1.0 1.0 0.0 4.0
Cos.diff() 2.0 3.0 3.0 1.0 12.0
Digit.add(Digit) 1.0 1.0 1.0 0.0 3.0
Digit.deO() 1.0 1.0 1.0 0.0 4.0
Digit.diff() 1.0 1.0 1.0 0.0 3.0
Digit.Digit() 1.0 1.0 1.0 0.0 2.0
Digit.Digit(BigInteger) 1.0 1.0 1.0 0.0 3.0
Digit.Digit(BigInteger,BigInteger,BigInteger,BigInteger) 1.0 1.0 1.0 0.0 7.0
Digit.Digit(String) 1.0 1.0 1.0 0.0 6.0
Digit.equals(Object) 1.0 3.0 3.0 0.0 6.0
Digit.getCoef() 1.0 1.0 1.0 0.0 3.0
Digit.getIndex() 1.0 1.0 1.0 0.0 3.0
Digit.getIndexC() 1.0 1.0 1.0 0.0 3.0
Digit.getIndexS() 1.0 1.0 1.0 0.0 3.0
Digit.hashCode() 1.0 1.0 1.0 0.0 3.0
Digit.multi(Digit) 1.0 1.0 1.0 0.0 10.0
Digit.on(Digit) 1.0 1.0 1.0 0.0 6.0
Digit.outD() 2.0 16.0 16.0 9.0 38.0
Digit.setCoef(BigInteger) 1.0 1.0 1.0 0.0 3.0
Digit.setIndex(BigInteger) 1.0 1.0 1.0 0.0 3.0
Digit.setIndexC(BigInteger) 1.0 1.0 1.0 0.0 3.0
Digit.setIndexS(BigInteger) 1.0 1.0 1.0 0.0 3.0
Item.addI(Digit) 1.0 1.0 1.0 0.0 3.0
Item.diff() 1.0 2.0 2.0 1.0 42.0
Item.Item() 1.0 1.0 1.0 0.0 2.0
Item.outI() 1.0 2.0 2.0 1.0 7.0
Main.findWF(String) 1.0 2.0 2.0 1.0 17.0
Main.main(String[]) 1.0 11.0 11.0 10.0 63.0
Main.mod(String) 1.0 1.0 1.0 0.0 14.0
Main.print(Object) 1.0 1.0 1.0 0.0 3.0
Main.println(Object) 1.0 1.0 1.0 0.0 3.0
Mi.diff() 2.0 3.0 3.0 1.0 9.0
Mi.Mi() 1.0 1.0 1.0 0.0 2.0
Mi.Mi(BigInteger) 1.0 1.0 1.0 0.0 4.0
Mi.Mi(BigInteger,BigInteger) 1.0 1.0 1.0 0.0 4.0
Mi.Mi(String) 1.0 2.0 2.0 1.0 10.0
Poly.addP(Digit) 1.0 2.0 2.0 1.0 9.0
Poly.deOut() 1.0 1.0 1.0 0.0 8.0
Poly.out() 1.0 2.0 4.0 3.0 17.0
Poly.out1() 1.0 1.0 1.0 0.0 3.0
Poly.out2() 1.0 1.0 1.0 0.0 3.0
Poly.out3() 1.0 1.0 1.0 0.0 3.0
Poly.Poly() 1.0 1.0 1.0 0.0 2.0
Poly.Poly(Digit,Digit,Digit) 1.0 1.0 1.0 0.0 5.0
Sin.diff() 2.0 3.0 3.0 1.0 11.0
Sin.Sin() 1.0 1.0 1.0 0.0 2.0
Sin.Sin(BigInteger) 1.0 1.0 1.0 0.0 4.0
Sin.Sin(String) 1.0 1.0 1.0 0.0 4.0
Total 54.0 89.0 91.0 30.0 391.0
Average 1.08 1.78 1.82 0.6 7.82

可以明显看出,我分了很多类,但其实本质都是一个——四元组。复杂度看上去不高的原因就是因为被分担了,实际上是“虚低”。

至于那个Digit里面很复杂的outD方法,那是我在输出时为了化简,把所有“项”合成了一个四元组试图提高性能分。

UML类图

扩展DIT

各种函数继承于Digit类,Digit类基本同时充当了“接口”的作用。

2.2 应用对象创建模式

选择了把不同类型的函数分装,在分析表达式的时候还用上了类似工厂模式的方法,不过没有把工厂独立出来。我用正则表达式分析表达式,先将式子拆成数个项,最后再将项拆成各个因子。判断WF也用的正则表达式“判对”。

依然没有考虑拓展性,因为没有时间。这一次的作业可以说是见招拆招,得分就万万岁了。

2.3 BUG分析

本次作业出现了两个BUG:

  • 判断WF时去掉了所有\s,没有考虑非法空白符的情况
  • 优化输出时竟然直接字符串替换"x**2"->"x*x",直接导致x**21一类光荣牺牲

两个错误都是考虑不周全的错,而第二个错误则是画蛇添足。

2.4 心得体会

这一次作业让我学到了许多。

  • 浅拷贝、深拷贝。虽然这时我还没有了解这些是什么意思,但我至少已经体会到了对引用类型来说,单纯的赋值只是地址的交接,实际上指向的是一个对象。这让我吃了不少苦头,BUG修了老半天。
  • 我这次由于时间和精力原因,基本没有进行测试,这也埋下了祸根。
  • 不要画蛇添足,一切修改都要充分考虑后再进行。
  • 自己的英语属实捉急,起名全是乱起(比如项用的Item),没有用拼音大概是最后的倔强了吧(Mi就是拼音)。

3.第三次作业

第三次作业是表达式求导迭代的最终进化体,加上了嵌套和比较复杂的WF判断。虽然代码完全重构,但有了前两次思路的支撑,这一次的构思并没有花上多少时间。

因为自知能力不足,所以一开始是准备完全放弃优化的。可后面看到
$$
(((0)+0)+0sin((0))+0(0)1cos((0)))
$$
这种实在是浑身难受,于是决定把幂函数和常数合并了、括号适当去除。

3.1 基于度量的程序结构分析

以下是含simplify方法的度量。

class OCavg WMC CSOA LOC
Basic 1.0 4.0 20.0 18.0
Cos 1.8 18.0 32.0 89.0
Digit 1.14 8.0 27.0 30.0
Eno 1.71 12.0 21.0 71.0
Item 4.29 30.0 28.0 147.0
Main 1.0 1.0 13.0 5.0
Mi 1.875 15.0 29.0 58.0
Null 1.0 4.0 24.0 14.0
Poly 3.0 27.0 30.0 140.0
Sin 1.8 18.0 32.0 88.0
Total 137.0 256.0 660.0
Average 2.04 13.7 25.6 66.0

这次用了一个“Basic”接口提供求导和toString方法(以及simplify),Main里有许多静态方法,对表达式进行预处理。Eno是一个简化用的类。其他的就是因子、项、表达式类,全部实现Basic。

具体思路见3.2。

method ev(G) iv(G) v(G) CONTROL LOC
Basic.Basic() 1.0 1.0 1.0 0.0 3.0
Basic.Basic(BigInteger) 1.0 1.0 1.0 0.0 3.0
Basic.getCoef() 1.0 1.0 1.0 0.0 3.0
Basic.setCoef(BigInteger) 1.0 1.0 1.0 0.0 3.0
Cos.Cos(Basic) 1.0 1.0 1.0 0.0 5.0
Cos.Cos(BigInteger) 1.0 1.0 1.0 0.0 4.0
Cos.Cos(BigInteger,BigInteger,Basic) 1.0 1.0 1.0 0.0 5.0
Cos.Cos(BigInteger,String) 1.0 1.0 1.0 0.0 6.0
Cos.diff() 2.0 1.0 2.0 1.0 13.0
Cos.getIndex() 1.0 1.0 1.0 0.0 3.0
Cos.ifWF(String) 1.0 2.0 2.0 1.0 16.0
Cos.setIndex(BigInteger) 1.0 1.0 1.0 0.0 3.0
Cos.simplify() 3.0 3.0 3.0 2.0 10.0
Cos.toString() 3.0 4.0 5.0 4.0 20.0
Digit.diff() 1.0 1.0 1.0 0.0 3.0
Digit.Digit() 1.0 1.0 1.0 0.0 3.0
Digit.Digit(BigInteger) 1.0 1.0 1.0 0.0 3.0
Digit.Digit(String) 1.0 1.0 1.0 0.0 3.0
Digit.mulD(Digit) 1.0 1.0 1.0 0.0 5.0
Digit.simplify() 2.0 1.0 2.0 1.0 8.0
Digit.toString() 1.0 1.0 1.0 0.0 3.0
Eno.Eno() 1.0 1.0 1.0 0.0 2.0
Eno.Eno(List,String) 1.0 1.0 1.0 0.0 4.0
Eno.getElements() 1.0 1.0 1.0 0.0 3.0
Eno.getPoly() 1.0 1.0 1.0 0.0 3.0
Eno.preWF(String) 1.0 5.0 5.0 4.0 36.0
Eno.println(String) 1.0 1.0 1.0 0.0 3.0
Eno.pro3() 1.0 3.0 3.0 1.0 16.0
Item.diff() 1.0 4.0 4.0 3.0 15.0
Item.Item() 1.0 1.0 1.0 0.0 3.0
Item.Item(Basic) 1.0 1.0 1.0 0.0 4.0
Item.Item(String,List) 1.0 10.0 10.0 9.0 48.0
Item.mulI(Basic) 1.0 2.0 2.0 1.0 11.0
Item.simplify() 4.0 11.0 11.0 9.0 38.0
Item.toString() 1.0 2.0 2.0 1.0 7.0
Main.main(String[]) 1.0 1.0 1.0 0.0 3.0
Mi.diff() 2.0 2.0 2.0 1.0 7.0
Mi.getIndex() 1.0 1.0 1.0 0.0 3.0
Mi.Mi() 1.0 1.0 1.0 0.0 4.0
Mi.Mi(BigInteger) 1.0 1.0 1.0 0.0 4.0
Mi.Mi(BigInteger,BigInteger) 1.0 1.0 1.0 0.0 4.0
Mi.setIndex(BigInteger) 1.0 1.0 1.0 0.0 3.0
Mi.simplify() 3.0 2.0 3.0 2.0 11.0
Mi.toString() 3.0 4.0 5.0 4.0 19.0
Null.diff() 1.0 1.0 1.0 0.0 3.0
Null.Null() 1.0 1.0 1.0 0.0 3.0
Null.simplify() 1.0 1.0 1.0 0.0 3.0
Null.toString() 1.0 1.0 1.0 0.0 3.0
Poly.addP(Item) 1.0 1.0 1.0 0.0 6.0
Poly.addP(Poly) 1.0 1.0 1.0 0.0 6.0
Poly.diff() 1.0 2.0 2.0 1.0 7.0
Poly.ifWF(String) 1.0 2.0 2.0 1.0 27.0
Poly.Poly() 1.0 1.0 1.0 0.0 3.0
Poly.Poly(String) 1.0 2.0 2.0 1.0 20.0
Poly.preDo(String) 2.0 3.0 9.0 8.0 36.0
Poly.simplify() 3.0 6.0 7.0 6.0 25.0
Poly.toString() 1.0 2.0 2.0 1.0 7.0
Sin.diff() 2.0 1.0 2.0 1.0 11.0
Sin.getIndex() 1.0 1.0 1.0 0.0 3.0
Sin.ifWF(String) 1.0 2.0 2.0 1.0 17.0
Sin.setIndex(BigInteger) 1.0 1.0 1.0 0.0 3.0
Sin.simplify() 3.0 3.0 3.0 2.0 10.0
Sin.Sin(Basic) 1.0 1.0 1.0 0.0 5.0
Sin.Sin(BigInteger) 1.0 1.0 1.0 0.0 4.0
Sin.Sin(BigInteger,BigInteger,Basic) 1.0 1.0 1.0 0.0 5.0
Sin.Sin(BigInteger,String) 1.0 1.0 1.0 0.0 6.0
Sin.toString() 3.0 4.0 5.0 4.0 20.0
Total 89.0 125.0 139.0 70.0 609.0

拆得蛮散的,simplify有点庞大。

UML类图

3.2 应用对象创建模式

趁着这个小节,就把思路也记一下好了。程序主要分为以下几个部分:解析表达式(建立对象)、求导、(简化、)输出、判WF。总体来说,要比之前两次作业要“对象”得多,创建过程是工厂模式,只不过依然没有把工厂独立出类,而是统合在了Poly和Item类里。

我判断WF的思路是判错+判对,首先在main里排除非法字符、**和sin cos间空格等基础问题,再将字符串进行预处理后扔进Poly工厂。判对在Poly内递归进行,我首先将所有最外层括号连同里面的内容全部变为形如p1, s2, c3(分别代表表达式、sin、cos)一类表示,同时将其中内容存入一个list,数字就是对应角标。然后就可以开始递归构造表达式了,同时判断这一层的WF(如sin(sin(sin(x*x)))这种错误将在sin(x*x)这一层才报错)。然后先将表达式拆成项,每个项再分别再在Item工厂内构建(工厂同时接受刚才的list,以便递归构建表达式、sin、cos)。

其他的就没什么好说的了。

总体来说,有一定的抽象层次,但并没有抽离出来,还是不够“对象”,但已经有那么点味了。

3.3 BUG分析

本次中了十数刀,直接导致强测79,但其实都是优化错误。原因在于自己没有自测(面向样例和中测的DEBUG法)。

所以在修复的时候基本只改了一行,把输出化简结果改为输出求导结果。

具体而言,有以下几处:

  • 无脑字符串去除同时的首位括号,没考虑配不配对,(...)+(...)这种就会GG
  • sin(0)=0没问题,复制粘贴导致cos(0)也变成了0
  • 表达式因子去括号没有考虑到sin, cos里面需要括号

3.4 心得与体会

德不配位必有灾。胡乱优化会翻下阴沟。

对魔力只有E的弓阶就别老想去扛魔法。

二、圣杯战争——互测阶段

如果说公测还可以自守一方清净地,那互测真的就是八方神仙各显神通了,我一个小萌新只好在大佬中间瑟瑟发抖捞一点hack分。基础的全地图无差别测评机就不说了,一群大佬仗着艺高胆大摆出了24小时全自动轰炸机,简直叫人手无缚鸡之力。

我不是大佬,所以我全是一个一个手动黑箱评测(第三次作业的时候写了个七人一起评的PY)。面向数据互测,不读别人代码(有几个强者的拜读了一下),直接怼自己觉得可能出错的点,虽然不如全自动机高端,好歹也有很强的针对性,倒也别有一番韵味。

1. 第一次作业

这一次作业比较简单,我一共hack到了两个点。没什么特别的针对性,就是把各种情况都试了一遍。如各种可省略输出、0、**0、前置0之类的。

  • 将+-, -+处理成了+的错误
  • 在输出形如-1*x时把-漏掉了(是的,我也曾犯过这错)

2. 第二次作业

在这一次互测中,我甚至没有下载别人的文件,纯属盲炸。数据构造仍然是各种情况、尽量复杂化,边界WF一类的。

事后,我观察到自己hack到了这些点:

  • 错误理解”超过“,把边界判了WF
  • cos(x), sin(x)带指数时求导错误
  • cos(x)求导没带负号

看起来还是没什么参考价值。

3. 第三次作业

因为在强测中实在表现不佳,我所在的房间简直是血流成河。而在这时,我搭建起了无差别评测机,于是炸得越加爽快。

在这一场战斗中,我找到了所有人(包括自己)的BUG,并提交了hack7/7的数据上去找了找快感。这一次的BUG寻找战略依然是各种情况提交,不过这一次我也记录下了自己找到的所有BUG,以做统计(迫害)。

我大致找到了以下这些BUG:

  • 优化翻车把+优化没了(x+x->11)

  • 分析表达式时少读入项(x+x->1)

  • cos(0)=0

  • 括号嵌套过多时TLE

  • 左右括号错误

  • 边界情况误WF(指数50)

  • sin内括号嵌套表达式误WF(sin(((cos(x))+(sin(x)))))

  • sin中的x**2被优化成x*x导致WF

  • 在(前面加符号就会误判WF

  • 对表达式开头的负号处理错误,比如-x-x会输出(0-(1-1))

  • ……

似乎也没什么参考价值,不过告诉我们自测不到位,强测两行泪。

三、对比和心得体会

对比

难以望其项背,自己的问题蛮大的,说白了就是”对象“思想没有深入人心,面向过程的影子牵绊着我,对”抽象层次“的理解也不太到位。

心得体会

  • 在头发乱飞之余甚至感受到了快感可能就是编程的乐趣吧

  • 别乱优化,舍本逐末要不得

  • 多自测多自测多自测

  • 不要拖DDL不要拖DLL不要拖DDL

总体来说,我收获颇丰,希望自己在今后的单元里能改善问题,更抽象,更对象。

posted @ 2020-03-21 15:29  kumo  阅读(205)  评论(0)    收藏  举报