前三次OO作业总结
一、作业总结
前三次的任务都是表达式求导。这是我在高中就思考过的问题,但是很久都没有付诸实践,直到学习了“类”这个强大的工具。还有正则表达式,如果能适当使用,则不失为一个字符串格式检查的利器。真觉得有点编译原理的词法分析的赶脚。
从结果来看,不甚满意,尤其是第二次作业,由于一些不可预测的原因而没有做足够的测试,从而在强测阶段爆掉。这是个惨痛教训,争取以后的作业中不要再出现这种问题,把失掉的分弥补回来。
前两次作业,几乎没有面向对象的身影,而主要是锻炼程序的鲁棒性。格式识别中种种要求应接不暇,常常是顾此失彼。第三次作业本身就是递归和 is-a 的关系,这就为设计类、继承提供了逻辑基础。说了这么多还没有进入正题,下面分别对三次作业做个简要分析。
1、多项式求导(第一次作业)
其实这次作业完全是面向过程的。多项式可以表示为二元组的列表,这种形式关于求导这种运算是封闭的,也就是说求导依然产生二元组的表。由于涉及到合并同类项,所以使用映射表比较好,即hashmap
,指数作为键,系数作为值。这样,在查找、添加、合并的时间复杂度都很低,编码也方便。
性能分唯一得分方式就是合并同类项,系数1不输出,指数为1不输出,0项忽略。另外一个小点就是正项先输出。
这次只有一个主类,一Main到底。因为HashMap
已经可以存储所有多项式,就没考虑另定义类。
分模块进行:检查合法性,构造表达式,求导,输出四个主要方法。开始一个大问题是使用长正则匹配整个表达式,所以在字符串长度大于1000的时候会爆掉。后来改用拆项法,每次向后扫描符合条件的项。主要使用的工具是Pattern
,Matcher
。这是我第一次思考正则效率的问题,以前只觉得正则强大,没想到引擎是NPC的。这次记住了一个经验:少用星号,少用加号,少用竖线,多用中括号,多用问号。少用 (),多用 (?:)。
Main类结构:
度量:
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类结构:
度量:
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类图:
度量:
类:
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作业总结的更多相关文章
- 【作业】HansBug的前三次OO作业分析与小结
OO课程目前已经进行了三次的作业,容我在本文中做一点微小的工作. 第一次作业 第一次作业由于难度不大,所以笔者程序实际上写的也比较随意一些.(点击就送指导书~) 类图 程序的大致结构如下: 代码分析 ...
- 前三次OO作业小结
I used to be enamored of object-oriented programming. I'm now finding myself leaning toward believin ...
- 2018-北航-面向对象-前三次OO作业分析与小结
基于度量的程序结构分析 由于平时使用了NetBrains出品的IDEA作为IDE,在分析程序的时候我使用了IDEA的插件Metrics Reloaded.然而在使用时发现不懂得很多分析项目的含义,因此 ...
- 2018-北航-面向对象第三次OO作业分析与小结
1. 规格设计的发展历史 规格设计用于对程序设提供分解,抽象等的手段.在撰写代码规格的时候,需要对组成部件进行抽象. 在1960s,软件设计出现危机,例如Dijkstra提出了goto语句的种种危害, ...
- oo作业总结(一)
概述 经历了三次oo作业的洗礼,让我对java语言的强大以及面向对象编程有了初步的理解(当然,我是小白).本文接下来就将对自己这三次作业的代码进行分析以及分享自己的心路历程. 基础知识点考核 针对前三 ...
- OO前三次作业分析
一,第一次作业分析 度量分析: 第一次的oo作业按照常理来说是不应该有这么多的圈复杂度,但是由于第一次写的时候,完全不了解java的相关知识,按照c语言的方式来写,完全的根据指导书的逻辑,先写好了正确 ...
- OO前三次作业思考(第一次OO——Blog)
OO前三次作业总结 基于度量分析程序结构 由于三次作业较多,决定分析内容.功能最为复杂的第三次作业. 上图为第三次作业的类图.我使用了一个抽象类Factor,写了五个因子继承Factor,然后又单独开 ...
- OO前三次作业总结
一.第一次作业 1.程序设计分析  图1 第一次作业类图 ;//这句在VS2005中没问题,在2010中会报错. 解决方法:在资源管理器 “引用”项的"Microsoft.O ...
- 谈谈开发文本转URL小工具的思路
URL提供了一种定位互联网上任意资源的手段,由于采用HTTP协议的URL能在互联网上自由传播和使用,所以能大行其道.在软件开发.测试甚至部署的环节,URL几乎可以说无处不再,其中用来定位文本的URL数 ...
- rsync 同步mac机器目录数据到windows2008R2
openssh rsync -azvP --progress -e "ssh -p 6099" /ahwater/rsync/ ahwater@ip:/ahwater
- 在云服务器上体验Docker
1. 添加Docker repository key sh -c "wget -qO- https://get.docker.io/gpg | apt-key add -" 2. ...
- Python 标准库 -> Pprint 模块 -> 用于打印 Python 数据结构
使用 pprint 模块 pprint 模块( pretty printer ) 用于打印 Python 数据结构. 当你在命令行下打印特定数据结构时你会发现它很有用(输出格式比较整齐, 便于阅读). ...
- 问题:C# TrimEnd 去掉最后的逗号;结果: C#中 TrimEnd()用法
C#中 TrimEnd()用法 ①去除最后的逗号 string str=ab,cd,ef,; str=str.TrimEnd(new char[] { ',' }); 返回结果则是:ab,cd,ef ...
- leetcode559
class Solution { public: int maxDepth(Node* root) { ; if (root != NULL) { queue<Node> Q; Q.pus ...
- css垂直居中方法(二)
第四种方法: 这个方法把一些div的显示方式设置为表格,因此我们可以使用表格的vartial-align属性. 代码如下: <!doctype html> <html lang=&q ...
- 基于:Hadoop 2.6.0-cdh5.4.0 hive1.1.0 HBase 1.0.0-cdh5.4.0 关键配置文件
core-site.xml <configuration> <property> <name>fs.defaultFS</name> <value ...
- [解决问题] pandas读取csv文件报错OSError解决方案
python用padans.csv_read函数出现OSError: Initializing from file failed 问题:文件路径中存在中文 解决办法:修改文件路径名为全英文包括文件名