一、作业总结

前三次的任务都是表达式求导。这是我在高中就思考过的问题,但是很久都没有付诸实践,直到学习了“类”这个强大的工具。还有正则表达式,如果能适当使用,则不失为一个字符串格式检查的利器。真觉得有点编译原理的词法分析的赶脚。

从结果来看,不甚满意,尤其是第二次作业,由于一些不可预测的原因而没有做足够的测试,从而在强测阶段爆掉。这是个惨痛教训,争取以后的作业中不要再出现这种问题,把失掉的分弥补回来。

前两次作业,几乎没有面向对象的身影,而主要是锻炼程序的鲁棒性。格式识别中种种要求应接不暇,常常是顾此失彼。第三次作业本身就是递归和 is-a 的关系,这就为设计类、继承提供了逻辑基础。说了这么多还没有进入正题,下面分别对三次作业做个简要分析。

1、多项式求导(第一次作业)

其实这次作业完全是面向过程的。多项式可以表示为二元组的列表,这种形式关于求导这种运算是封闭的,也就是说求导依然产生二元组的表。由于涉及到合并同类项,所以使用映射表比较好,即hashmap,指数作为键,系数作为值。这样,在查找、添加、合并的时间复杂度都很低,编码也方便。

性能分唯一得分方式就是合并同类项,系数1不输出,指数为1不输出,0项忽略。另外一个小点就是正项先输出。

这次只有一个主类,一Main到底。因为HashMap已经可以存储所有多项式,就没考虑另定义类。

分模块进行:检查合法性,构造表达式,求导,输出四个主要方法。开始一个大问题是使用长正则匹配整个表达式,所以在字符串长度大于1000的时候会爆掉。后来改用拆项法,每次向后扫描符合条件的项。主要使用的工具是PatternMatcher。这是我第一次思考正则效率的问题,以前只觉得正则强大,没想到引擎是NPC的。这次记住了一个经验:少用星号,少用加号,少用竖线,多用中括号,多用问号。少用 (),多用 (?:)。

Main类结构:

image

度量:

Class OCavg WMC
Main 5 40
Method ev(G) iv(G) v(G)
Main.createPoly() 1 5 7
Main.exitError() 1 1 1
Main.func(int,int) 1 4 4
Main.judge() 1 7 8
Main.main(String[]) 1 1 2
Main.nextStart(int,int,boolean) 6 6 8
Main.output(HashMap<BigInteger, BigInteger>) 3 8 10
Main.qiuDao(HashMap<BigInteger, BigInteger>) 3 4 5

可以看出,在表达式判断合法性、构造和输出方面比较复杂,但我觉得这个比较正常,毕竟格式属于盲搜,而输出需要考虑不同的情况做优化。

互测被测出字符串末尾的加号问题。这是在Matcher扫描过程中出现的错误。这次总体比较满意,美中不足的就是性能分,在一个 -1*x上没有优化成 -x。

2、多项式、三角函数乘积求导(第二次作业)

这次栽了大跟头。由此可知程序测试在工程开发中的重要性。一个小小的replace错误导致强测加互测总共WA了10次。这个错误太致命了,由于split("\\+")是以加号分割字符串,而预处理阶段的干扰加号没有排干净,失之毫厘谬以千里。

这次有点面向对象的感觉,不过,依然几乎是一Main到底,唯一的一个内部类还是当结构体用的,它用来存储sin,cos,x的指数,作为Hashmap的键,系数作为值。在HashMap中,自定义类需要重写equals()hashcode()方法,初步使用了方法重写。扫描方法还和上次一样,只不过多了两种sin(x)和cos(x)。

优化仍然是合并同类项,但是还有sin(x)^2+cos(x)^2=1这种问题,当时没有过多考虑。后来认真研读了大佬们的算法,才知道这个公式有好多文章可以做。原来面向对象也是考数学的啊。

Main类结构:

image

度量:

Class OCavg WMC
Main 6.1 61
Main.Term 1.33 4
Method ev(G) iv(G) v(G)
Main.Term.Term(BigInteger,BigInteger,BigInteger) 1 1 1
Main.Term.equals(Object) 2 3 4
Main.Term.hashCode() 1 1 1
Main.createExpr() 5 9 11
Main.func(int,int) 1 4 4
Main.gTmD(boolean,String) 2 1 2
Main.judge() 1 9 13
Main.main(String[]) 1 3 3
Main.nextStart(int,int,boolean) 7 8 12
Main.output(HashMap<Term, BigInteger>) 1 15 17
Main.printWrongFormat() 1 1 1
Main.putTerm(HashMap<Term, BigInteger>,Term,BigInteger) 1 2 2
Main.qiuDao(HashMap<Term, BigInteger>) 3 5 6

仍然是和判断、构造、输出有关的函数,复杂度比较高。而且由于在增加了三角函数之后仍然一Main到底,平均复杂度提高了。

3、多项式、三角函数复合求导(第三次作业)

这一次吸取了前一次作业的教训,做了百余数据的测试。但还是被hack了,sin(-1)这种会WF,我的妈呀表达式忘了考虑常数为负数的问题。有惊无险,如果强测多几个这种点我就又玩完了。(正所谓bug是难以避免的,只有神仙才能做到完全没有bug,人与人之间差的只是出bug的几率。但是这个几率,能真正决定一个工程的成败)。

不过半天的思考架构也让我受益匪浅,至少能利用面向对象的思想,把一个具体的事物抽象出数据类型和操作了。真正的数据类型不仅仅是存储,而在于性质和操作的捆绑。

这次是真正体现面向对象威力的时刻。表达式本身就是个抽象概念,所以用了抽象类。表达式要参加运算的,所以定义了加、减、乘、复合四种运算作为具体方法。不同种类的表达式都是表达式,而且都有统一的加减乘复合这些运算,所以继承表达式类,这是 is-a 关系。求导,对不同样式的表达式来说规则不尽相同,而且是分层递归的,所以定义成抽象方法,在表达式类的子类中重写,这是多态。子类呢,分别定义基本表达式类(包括常数、幂函数、三角函数的乘方),加减类(两个表达式通过加减运算形成),乘法类(两个表达式通过乘法运算形成),复合类(两个函数复合的结果)。这样,在创建表达式的过程中,就会自动形成表达式树(二叉树),求导起来也特别方便,直接向下递归即可,而表达式树的叶子节点就是基本类。

这样做的好处是,在表达式格式识别、创建的过程中可以略去括号的影响,宏观考虑,递归向下。有点类似于数据结构课程中做的表达式计算器,当有括号时递归进行。

这次把类设计的各个知识点全部回顾了一遍,包括构造方法、方法重写、抽象类、继承、动态联编(多态)这些。我觉得这次作业是比较经典的面向对象练习,对OO思维是一种很好的锻炼。在前两次锻炼了程序的鲁棒性之后,第三次作业加深了对封装、继承的理解。继承并不仅仅是为了代码复用,还有一个重要作用就是可以实现运行时确定类型,这是OO的精髓所在。(顺带的,把C++中的虚函数、纯虚函数这些概念彻底搞懂了。C++,Java这俩语言感觉真是亲兄弟)

最初想用接口,但考虑到复用性,加、减、乘、复合这些方法无需在每个类中重新写一遍,就改用抽象类了。

UML类图:

image

度量:

类:

Class OCavg WMC
AddSub 1.2 6
Basic 3.75 15
Composite 1 4
Expr 3 24
Main 5.29 74
Multiply 1 4

方法:

Method ev(G) iv(G) v(G)
AddSub.AddSub(boolean,Expr,Expr) 1 1 1
AddSub.getExpr1() 1 1 1
AddSub.getExpr2() 1 1 1
AddSub.isAddOrSub() 1 1 1
AddSub.qiuDao() 2 2 2
Basic.Basic(int,BigInteger) 1 2 3
Basic.getExpClass() 1 1 1
Basic.getPara() 1 1 1
Basic.qiuDao() 10 10 12
Composite.Composite(Expr,Expr) 1 1 1
Composite.getInner() 1 1 1
Composite.getOuter() 1 1 1
Composite.qiuDao() 1 1 1
Expr.add(Expr) 4 4 5
Expr.composite(Expr) 7 7 7
Expr.equalsToOne() 1 2 2
Expr.equalsToZero() 1 2 2
Expr.isConstant() 1 2 2
Expr.isX() 1 3 3
Expr.multiply(Expr) 5 6 7
Expr.sub(Expr) 4 4 5
Main.add(Expr,Expr) 2 2 2
Main.brackets(String) 1 6 7
Main.conditionalPrint(Expr,String) 1 2 2
Main.create(String) 6 8 12
Main.find(Matcher,int,int,int,int) 3 3 5
Main.gI(String,int) 3 2 6
Main.isSin(Expr) 1 2 2
Main.judge(String) 2 10 17
Main.main(String[]) 1 3 3
Main.mul(Expr,Expr) 2 2 2
Main.nextStart(String,int,int,boolean) 7 8 12
Main.output(Expr) 1 16 16
Main.tooBigIndex(BigInteger) 1 3 3
Main.wrongFormat() 1 1 1
Multiply.Multiply(Expr,Expr) 1 1 1
Multiply.getExpr1() 1 1 1
Multiply.getExpr2() 1 1 1
Multiply.qiuDao() 1 1 1

虽然功能比上次复杂,但是OCavg,也就是平均复杂度反而降低了,可见使用类封装、继承的巨大优势。

二、学到了什么

首先就是正则表达式,包括正则的效率问题。然后就是程序的鲁棒性,实际工程中程序对各种不同状况做出的反应都应该符合用户需求。最最重要的就是面向对象的思维。

另外,oo强迫我改掉了不加空格的代码风格。这也是一个巨大的提升,毕竟看那种堆在一起,一个方法几百行,满屏goto等等的代码有多难受,你我都知道。

有痛失分数的懊悔,也有巨大的收获。在这种强大挑战面前,就要有把oo做成软工的思想准备(逃~

三、工具推荐

首先,我推荐用markdown写博客,博客园自带的编辑器超级恶心。可以先用typora编辑好,然后在博客园中设置:管理博客--设置默认编辑器--修改为markdown。之后,把你编辑好的markdown复制到博客的文本框中。可以保存为草稿预览一下。

1、UML工具

idea自带的,选中所有类,右键--Diagrams--Show Diagram

2、复杂度分析工具:MetricsReloaded

idea菜单栏--文件--设置--Plugins--搜索MetricsReloaded--Install--重启idea

安装成功后,打开工程,选择你需要分析的类,然后右键--分析--Calculate Metrics

3、CSV、markdown、HTML、XLS表格转码

网址:tableconvert

idea的Metrics分析完成后,我们可以选择导出到文件(左下角一栏,特别不显眼的地方)。导出的文件是一个CSV格式,我们把它用Excel打开,然后选择一个表格(注意你需要的表格边界,CSV一般没有sheet),复制到剪贴板。

之后打开上述网站,单击上方import选项,把你剪贴板里的东西粘贴到文本框,确定。下面的导出格式选择markdown。OK,最后把生成的markdown表格复制到你的md文件中。

不过,CSV在typora中是可以直接转码的,typora大法好啊!(天灭TinyMCE,退TinyMCE保平安

4、图片--base64转码工具

网址:xpcha

这是一个极好的base64和图片相互转码的工具。我们知道,markdown的图片要么用超链接链接到磁盘、网站,要么用内嵌模式即base64码。而链接到磁盘则无法上传,链接到URL则无法链接本地图片,还需要图床等工具,极不方便。所以,我们用这个网站把图片转化为base64之后,在文件末尾设置链接,在行内进行引用。具体可参考CSDN或简书上的教程。

前三次OO作业总结的更多相关文章

  1. 【作业】HansBug的前三次OO作业分析与小结

    OO课程目前已经进行了三次的作业,容我在本文中做一点微小的工作. 第一次作业 第一次作业由于难度不大,所以笔者程序实际上写的也比较随意一些.(点击就送指导书~) 类图 程序的大致结构如下: 代码分析 ...

  2. 前三次OO作业小结

    I used to be enamored of object-oriented programming. I'm now finding myself leaning toward believin ...

  3. 2018-北航-面向对象-前三次OO作业分析与小结

    基于度量的程序结构分析 由于平时使用了NetBrains出品的IDEA作为IDE,在分析程序的时候我使用了IDEA的插件Metrics Reloaded.然而在使用时发现不懂得很多分析项目的含义,因此 ...

  4. 2018-北航-面向对象第三次OO作业分析与小结

    1. 规格设计的发展历史 规格设计用于对程序设提供分解,抽象等的手段.在撰写代码规格的时候,需要对组成部件进行抽象. 在1960s,软件设计出现危机,例如Dijkstra提出了goto语句的种种危害, ...

  5. oo作业总结(一)

    概述 经历了三次oo作业的洗礼,让我对java语言的强大以及面向对象编程有了初步的理解(当然,我是小白).本文接下来就将对自己这三次作业的代码进行分析以及分享自己的心路历程. 基础知识点考核 针对前三 ...

  6. OO前三次作业分析

    一,第一次作业分析 度量分析: 第一次的oo作业按照常理来说是不应该有这么多的圈复杂度,但是由于第一次写的时候,完全不了解java的相关知识,按照c语言的方式来写,完全的根据指导书的逻辑,先写好了正确 ...

  7. OO前三次作业思考(第一次OO——Blog)

    OO前三次作业总结 基于度量分析程序结构 由于三次作业较多,决定分析内容.功能最为复杂的第三次作业. 上图为第三次作业的类图.我使用了一个抽象类Factor,写了五个因子继承Factor,然后又单独开 ...

  8. OO前三次作业总结

    一.第一次作业 1.程序设计分析 ![img](s1.ax1x.com/2018/04/02/CSgoSU.png) 图1 第一次作业类图 ![name](https://images2018.cnb ...

  9. 第一次码java感想及前三次作业总结

    写在前面 嗯,首先是java,这学期第一次oo作业布置下来的周末才开始看的,第一次作业因此写得有些手忙脚乱.不过大概看了一遍后发现比c好用,入门更简单吧,好多操作直接import一下就能用了,码代码的 ...

随机推荐

  1. 01-19asp.net网站--关于“应用程序中的服务器错误(需添加"Jquery"ScriptRescourseMapping)”

    一般打开网页进行加载时(有缓存),会弹出以下对话框. 但是如果网页加载后出现以下错误,就是应用程序的问题了.如果出现这种问题,就需要在安装Csharp的根目录下,找到一个名为.dll结尾的Jquery ...

  2. leetcode590

    树的后序遍历. class Solution { public: vector<Node> Tree; void postTree(Node node) { for (auto n : n ...

  3. leetcode443

    使用两个数组分别记录字符和对应的数字,然后清除原来的vector,重新向里面添加元素.注意判断1个字符时,不将'1'加入vector. int compress(vector<char>& ...

  4. Xshell的简单使用

    1.下载并安装 Xshell 4打开后如下图所示,会出现一个界面框,这个界面框类似于DOS的界面,需要操控远程的主机,都是通过这个界面进行操作. 2在这个界面左上角的位置有一个文件按钮,点击这个按钮. ...

  5. 解决ftp无法连接登录linux的办法

    1. 首先安装vsftpd 命令:yum -y install vsftpd 之后开启服务:service vsftpd start 2.关闭防火墙 1) 重启后生效 开启: chkconfig ip ...

  6. Spring IOC容器解析及实现原理

    最近一段时间,“容器”两个字一直萦绕在我的耳边,甚至是吃饭.睡觉的时候都在我脑子里蹦来蹦去的.随着这些天一次次的交流.讨论,对于容器的理解也逐渐加深.理论上的东西终归要落实到实践,今天就借助sprin ...

  7. 添加超级链接为什么用a标签

    a是anchor的简写,中文意思是锚点,而锚点的引申意思是连接,link已经被html占用了,只能用a来表示连接了.

  8. LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);

    reinterpret_cast代表强制转化,即把pNMHDR强制转化成LPNMITEMACTIVATE类型的. reinterpret_cast<type-id> (expression ...

  9. bzoj1735 [Usaco2005 jan]Muddy Fields 泥泞的牧场

    传送门 分析 我们知道对于没有障碍的情况就是将横轴点于纵轴点连边 于是对于这种有障碍的情况我们还是分横轴纵轴考虑 只不过对于有障碍的一整条分为若干个无障碍小段来处理 然后将标号小段连边,跑最大匹配即可 ...

  10. 按位操作符(Bitwise operators)

    按位操作符(Bitwise operators) 将其操作数(operands)当作32位的比特序列(由0和1组成),而不是十进制.十六进制或八进制数值.例如,十进制数9,用二进制表示则为1001.按 ...