OO是个借助Java交我们面向对象的课,可是萌新们总是喜欢带着面向过程的脑子去写求导,然后就是各种一面(main)到底、各种方法杂糅,然后就是被hack的很惨。

第一次作业:萌新入门面向对象

题目分析

题目其实不难,最难不过是这种东西的求导:

+4*x - -x^2 + x  

其实很好办啊,直接超级长的一个正则表达式莽,如图。

String pattern = "...";//好长的一段
String longPattern = "pattern(pattern)+";

于是这次作业那些一百多个字符长的正则表达式就来了,思想就是整体匹配,然后就被hack地爆栈了(GG。当然,正则可不是给你这样操作的,正则更加适合于那些特征性强的长度也不是很长的串的识别。一个更好的办法是逐项匹配,匹配到一项就存起来再匹配下一项,然后求导化简一气呵成。

整体思路是对读入进行一些处理,比如排除掉一些错误字符、判断空格位置和合法性、格式化字符串以便逐项匹配,然后利用简单的正则表达式匹配第一项并实例化一个Poly类,利用通项a*x^b进行储存(这里可以利用HashMap或者暴力for循环合并下同类项)。然后就可以求导了。输出的时候也要分类:将系数为0,系数为1和系数为-1的情况;指数为0,指数为1的情况分类输出。

题目不难,思路也清楚,问题在于不知道面向对象是个什么东西。就比如我的架构:

我把逐项匹配、输出优化全部放在了主类PolynomialDerivation里面,然后复杂度就爆炸了,单个方法也是逼近60行边界。虽然说类比较少,但是我main里面什么都有,各种功能胡乱炖,这代码风格就很差了,读起来颇有C的感觉。

发现问题

主要是大家的写法都很C-style,功能混杂,面向过程。

次要是正则表达式不够完善,比如少了一个[\t ],没有处理非法字符,当然也有一些刀友采取了不是很好的处理输入的方案,导致一些诡异的数据点会报错,比如 +或者500个+x。

再次要问题是刀不够长,我一开始采取简单的蒙眼扔弹法,手动造数据,然后蒙着眼睛给房间七兄弟扔一发,结果就是伤害真的不行。后来通过看正则表达式也有所收获,但总体还是消耗体力来刀人。

第二次作业:萌新尝试面向对象

题目分析

第二次作业学乖了,考虑到有幂函数和三角函数两种情况,同学们开始用继承啊接口啊操作,或者,用一种“面向题目”的办法:找到一个通项a*xb*sin(x)c*cos(x)d,然后甚至可以不用对sin、cos的类进行求导,而是直接对这个通项类(Item类)进行求导,得到三项,分别都为Item类,然后通过HashMap或者for循环合并同类项完事(dalao:加一层三角恒等变换也行)。这个看似巧妙的办法很好的简化了类的功能需求,”面向题目“地解决了问题,将可拓展性抛掷脑后。我相信很多人也是这样写的;而这显然有违代码风格中的“开闭原则”——可以基于你的代码进行拓展,但不能修改你的代码,毕竟通项这种东西可遇而不可求。

这一节标题是尝试面向对象,那么相比第一次的main.c,这次又有什么不同呢。

  • 将功能分离,对于+3*x^2*sin(x)-cos(x),将其分为+3*x^2*sin(x)这样的一个实例,再分为3和x^2和sin(x)这三个基类的实例,拥有了基本的界限和分工
  • 对于类内部保持了封闭和不可见,至少在语法层面有了封装

  • 接口和抽象类的使用开始出现

  • dalao们的其他骚操作,比如Factory和assert

我的设计也更加的有层次感,虽然依旧存在着主类中功能较多,类较少,功能混杂,“面向题目”过于明显而扩展性较差的问题,隐约感觉到我第三次作业要爆肝重构了。

这次作业中复杂性比较高的方法是在一长串输入的字符串找到一个个的项(即通项),因为这个方法不仅仅涉及了循环处理字符串寻找通项,也兼职了用matcher.group()方法取得其中的有效信息的任务,导致这个方法比较复杂;但是它可以被拆解成两个方法:一个负责matcher.find(),另一个负责用group()取得数据,他们之间通过传递Matcher类的参数来沟通。

发现问题

这次的强测错了几个点,然后分到了腥风血雨的C组,各种你捅我一刀我还你两刀,最后被砍七刀。后来公布数据之后我才发现,当初一个小小的疏忽,贪图简单把1*x这种系数为1的情况的化简写成了replace("1*","")。单纯的将1*给替换掉,乍一看好像是没有错的,但是我却忽略了当系数为11、21、31...的时候,乃至所有系数个位为1的项,甚至x^1*sin(x)这样的项,几乎都是错的。

第三次作业:萌新胡乱面向对象

题目分析

写完第二次作业以后,我就开始了对代码的重构,因为自己对于面向对象有有了新的理解,以前代码许多不规范不区分的东西,都需要重新分门别类,于是我将输入模块拿出,将一些过长的方法重新理清。

首先是不能依靠正则表达式了,Java的正则仿佛不能处理嵌套的问题,而且由于第一次作业就有深层递归导致爆栈的问题出现,这种一层又一层的就不能直接一个正则往上写了,而需要剥洋葱。

其次是类的职责需要分的更加清楚,我现在有一种想法就是把题目中所有出现过的对象都作为一个类,不论大小不论耦合,然后再去分析其中的关系。就比如逛菜市场,先无脑把所有的每一种的食材都建立一个类,然后再考虑这个属于鱼、那个属于青菜这样的分类,建立父类或者接口。这样的分类法,应该能更好的将对象分离。

考虑第三次的任务,我和很多同学一样,建立了一棵树,树中的元素为两种类:Expression类(负责解析形如1+1的式子),Item类(负责解析形如1*1的式子),并且他们继承自一个公共的父类Factor(负责处理sin(x)这样的简单表达式)。

我的大概想法是这样:一个表达式解析成Expression类,然后按照+号或者-号分割成若干个Item类,Item类中再依据*号切割成若干个Expression类……直到最后剩下一个形如1、x、sin(x)这样的简单因子,但是在处理过程中,慢慢的发现自己设计的不合理之处:第一Expression类同时也肩负着处理形如sin(((x+4)*cos(x)))这样的表达式中的sin部分的责任,第二Expression类结点的子结点只能为Item类,Item类的子结点只能为Expression类。这两个问题在我实际在写的时候才发现,处理起来逻辑上比较麻烦,甚至需要东拼西凑。也正因此,在写完之后的测试过程中,我经历了长期的修补过程;最后的程序也不是完美的。

最后写出来的类比较少(相比其他同学),只有六个;我分析了下,这是因为自己类的作用混合性很高,比如Factor类可以识别并处理简单因子,同时又包含了三种简单因子的求导方法,更还有Expression类和Item类共用的切割字符串中因子的方法;如果重构的话,我会将其分为两个类:真正的Factor类(负责处理1、x、sin(x)这样的简单因子)和一个Expression、Item类的公共父类,同时Factor类又可以包含三个子类,分别用来处理纯数字、x的幂、三角函数的幂。“胡乱面向对象”指的就是我这样各种元素混在一起写成一个类然后出现各种各样的bug的操作。

发现问题

在互测中,我自己发现的bug就是因为自己胡乱一波操作导致的:Expression类由于兼职了处理形如sin(((x+4)*cos(x)))这样的表达式中的sin部分的责任,而在按照括号切割的时候,面对sin(x)*(x)这种伪sin(x)型的式子,就出现了bug;在面对sin(cos(((x))))这种多个括号嵌套三角函数的时候,也出现了bug。所以这些bug如果好好写对象,不要一股脑用面向过程的思想去莽,也不会出现。

除了bug之外,就是方法和类的复杂性和耦合性比较高,如图。

从图中可以看出,几乎每个类都和其他类有直接的关系,所以关系错综复杂;这也是我设计的缺漏导致的代码风格的不佳。

方法复杂度我只展示了复杂度最高的的几个方法,其中主要是切割字符串cutString方法、求导的derivate方法比较复杂。因为cutString方法我是用for循环来遍历字符串并且用ArrayList来接收结果,而derivate方法涉及到了多重的递归调用和复杂的乘法求导的法则,故这两个方法比较复杂。

另外,由于使用的类相对比较少,类之间的平行性也不是很强,我也懒得去写接口了,似乎没有这个的必要。

互测中发现的bug,并没有特别强的典型性,应该都是因为架构的小瑕疵导致的,不过根据观察,计算错误的比较少,反而是因为某些特殊数据导致的爆栈或异常然后输出了WRONG FORMAT!。

在三次的刀人的过程中,我找bug的能力也越来越强,虽然依旧停留在用炸弹炸鱼的老思想,但是用的装备越来越成熟了。第一次作业的时候是典型的蒙眼手动炸人,第二次开始写了一些基于命令行的脚本,能够自动根据输入将所有人的输出都列出来,第三次借用了别人的数据生成器然后自动带入数据比对出大家的对与错。虽然不知道以后还用不用得着这种工具,但是自己也懂得了很多课外的、其他语言的用法,增长了见识。同时结合自动数据和手动的点对点数据的轰炸,在刀人方面也成了一个“狼人”。

OO随笔之魔鬼的第一单元——多项式求导的更多相关文章

  1. OO第一单元表达式求导作业总结

    第一次作业 功能描述: 对输入的表达式进行求导计算和格式正误判断   思路: 一开始的想法是想写一个大正则找到一个通项式,通过这个多项式来判断WRONG FORMAT,结果发现正则写的总是不完善,会漏 ...

  2. OO第一单元总结——求导

    一.基于度量分析程序结构 (一)第一次作业 (1)设计思路 本次作业只涉及到简单幂函数通过加减运算而复合而成的函数,因此笔者自然的把函数分成了函数本体以及单个的项两个部分,在笔者的设计中两个类的功能如 ...

  3. BUAA-OO-第一单元表达式求导作业总结

    figure:first-child { margin-top: -20px; } #write ol, #write ul { position: relative; } img { max-wid ...

  4. OO第一单元总结-多项式求导

    OO第一单元总结-多项式求导 一.第一.第二次作业总结 因为前两次作业设计复杂度差别不大,因而放在这里统一总结. 基于度量分析程序结构: 前两次作业确实存在缺乏可拓展设计的构想,基本还是面向过程的思维 ...

  5. OO_多项式求导_单元总结

    概述: 面向对象第一单元的作业是三次难度依次递增的多项式求导.第一次作业是仅包含带符号整数和幂函数的多项式求导,例如:-1+xˆ233-xˆ06:第二次是在前面的基础上增加了三角函数的求导,例如:-1 ...

  6. 多项式求导系列——OO Unit1分析和总结

    一.摘要 本文是BUAA OO课程Unit1在课程讲授.三次作业完成.自测和互测时发现的问题,以及倾听别人的思路分享所引起个人的一些思考的总结性博客.本文第二部分介绍三次作业的设计思路,主要以类图的形 ...

  7. 2019年北航OO第1单元(表达式求导)总结

    2019年北航OO第1单元(表达式求导)总结 1 基于度量的程序结构分析 量化指标及分析 以下是三次作业的量化指标统计: 关于图中指标在这里简要介绍一下: ev(G):基本复杂度,用来衡量程序非结构化 ...

  8. BUAA_OO Summary——多项式求导问题

    从C.DS.计组一路折磨过来, 几乎都在采用过程化.函数式的编程思想.初接触面向对象的项目开发,经过了三周的对多项式求导问题的迭代开发,经历了设计.coding.测评环节,算是对面向对象有了一定的认识 ...

  9. OO第一单元总结__多项式求导问题

    作业一.含幂函数的简单多项式的求导 (1)基于度量的程序结构分析 1. 统计信息图: 2. 结构信息图: 3. 复杂度分析 基本复杂度(Essential Complexity (ev(G)).模块设 ...

随机推荐

  1. 美图录爬虫(requests模块,re模块)

    Python 爬虫 最近学正则表达式,刚好知道这个网站美图录,就做了个爬虫拿来练练手,说一说遇到的问题 一 404问题 问题: 由于图片显示页面是分页的,每一页展示5张图片,为了方便没有每次去获取下一 ...

  2. linux安装nginx 并配置文件服务器和代理服务器

    linux安装nginx搭建服务并实现文件服务器和代理服务器配置 1.课题的背景和意义 由于编码过程中需要进行文件上传服务,文件上传后 需要有http资源的路径需要访问.原则上可以通过Apache . ...

  3. 别再面向 for 循环编程了,JDK 自带的观察者模式就很香!

    大家好,你还在面向 for 循环编程吗? 还有谁不会用观察者模式吗? 本篇栈长带来<观察者模式>理论及实战- 什么是观察者模式? 观察者模式(Observer Pattern)定义了对象间 ...

  4. E. 【例题5】平铺方案

    E . [ 例 题 5 ] 平 铺 方 案 E. [例题5]平铺方案 E.[例题5]平铺方案 解析 由于最近赶进度,解析写的就很简略 通过推算得出递推式 a [ i ] = a [ i − 1 ] + ...

  5. [Fundamental of Power Electronics]-PART I-3.稳态等效电路建模,损耗和效率-3.4 如何获得模型的输入端口

    3.4 如何获得模型的输入端口 Fig 3.16 Buck converter example 让我们尝试使用3.3.3节的步骤来推导图3.16所示的Buck变换器的模型.电感绕组电阻同样由串联电阻\ ...

  6. [Fundamental of Power Electronics]-PART I-4.开关实现-0 序

    4 开关实现 在前面的章节中我们已经看到,可以使用晶体管,二极管来作为Buck,Boost和其他一些DC-DC变换器的开关元件.也许有人会想为什么会这样,以及通常如何实现半导体的开关.这些都是值得被提 ...

  7. SpringBoot整合Swagger2及使用

    简介 swagger是一个流行的API开发框架,这个框架以"开放API声明"(OpenAPI Specification,OAS)为基础, 对整个API的开发周期都提供了相应的解决 ...

  8. Windows Service 2016 Datacenter\Stand\Embedded激活方法

    安装好系统后连入互联网之后使用管理员身份打开命令行 输入命令 slmgr /skms kms.03k.org 弹出窗口提示模式修改成功后再输入命令:slmgr /ato 以下为各个版本的key 版本: ...

  9. ASP.NET Core中使用令牌桶限流

    在限流时一般会限制每秒或每分钟的请求数,简单点一般会采用计数器算法,这种算法实现相对简单,也很高效,但是无法应对瞬时的突发流量. 比如限流每秒100次请求,绝大多数的时间里都不会超过这个数,但是偶尔某 ...

  10. 案例 | 荔枝微课基于 kubernetes 搭建分布式压测系统

    王诚强,荔枝微课基础架构负责人.热衷于基础技术研发推广,致力于提供稳定高效的基础架构,推进了荔枝微课集群化从0到1的发展,云原生架构持续演进的实践者. 本文根据2021年4月10日深圳站举办的[腾讯云 ...