第三次寒假作业
本次作业的github链接。
相同部分
1) 两位合作者的学号和对应博客主页链接
赵畅 111500206 http://www.cnblogs.com/ZCplayground/
胡绪佩 031602114 http://www.cnblogs.com/heihuifei/
2) 描述实现设计思路
大的模块分为,题目模块,外部函数,主函数三个。下面对各个模块进行详细介绍。
题目模块,主要实现在了Expression.h这个头文件中。其核心是表达式类: Expression类的类声明和类方法定义。在这里实现的功能是:随机生成表达式,并且求出这个随机式子的运算结果,以下是一些本模块解决的技术细节问题:
- srand和rand实际上是“伪随机”,我们努力做到了与真正意义上的“随机”相近的效果。
- 实现了数字、操作符、表达式的格式完全是随机产生的,长度也是范围内可变的。但有一个缺点就是括号里目前只有两个数,且还没实现括号里还有括号的情况。
- 防止了出现“除以零”这种情况的产生。
- 防止出现“除不尽”这种情况的产生。
- 对数字“10”,进行了正确的处理。
- 显示给用户的是中缀表达式。通过转换成逆波兰式求出随机生成的表达式的值。
外部函数模块,主要是ExtendFunction.h。其中实现了语言版本选择、用户输入答案判定的功能。技术细节方面:
- 版本选择、用户自定题目数量,这两项有做输入检测,防止意外的输入错误导致程序错误运行
主函数,main.cpp。实现了主体功能。除了调用其他文件中的函数,还负责了最后结果统计的功能。
3) 你们订立的编码规范
整个代码规范我也放到github上了,Coding Standards。详细的可以clone下来看看。我们的做法是从网上寻找一份C++编码规范,依照比较详细的规范,从中选择一些适合的部分,略作修改和整理,组成我们这个小程序的规范。
我挑几个比较重要、在此次编码中用得最多的来说说:
- 相对独立的程序块之间、变量说明之后必须加空行。
为了让人知道这是相互独立的功能模块。增加代码可读性。
-
一行只写一条语句。
-
if、for、do、while、case、switch、default等语句自占一行,且if、for、do、while等语句的执行语句部分无论多少都要加括号{}。
-
使用操作符对多个关键字、变量、常量进行操作时,要加入空格使得代码更加清晰。
这三条也是为了可读性。
-
程序必须具有一定的注释,便于对程序的理解。注释需要简介明了准确,不然有可能起反作用。一定要边写代码边注释。
-
标识符命名原则。变量:采取小驼峰法,函数名、类名:采取大驼峰法,数组名:采取小驼峰法,并且用下划线分隔单词。
与个人单打独斗不同,这两点在多人合作中十分重要。
4) 程序实现和结果测试的截图
第一个截图主要体现了中文界面。
第二个截图主要体现了输入检测模块和英文界面。
5) github的提交链接和提交日志截图
本次作业的github链接。
6) 两位同学的分工和协作证据截图
分工方面,我负责了main.cpp、Expression.h、stack.h三个文件的代码编写工作。绪佩负责了ExtendFunction.h的大部分代码,我负责少许润色修改工作。代码规范的doc文件大部分由绪佩编辑而成,我负责检查、修改。
截图我就上传一张特别有意思的。可以看到我们那天通过分享桌面一起工作到了半夜三点。那天的午夜12点我解决了一个bug,想分享给胡同学看,谁知在那会又找到了一个更大的bug,所以我们又开始一起debug。没想到一开始debug就直接工作到了凌晨三点,才攻克了那个难关,完成了算法的主体部分,我们两个人都很高兴。第二天早上,就发布了Preview version,预览版本。
差异部分:
1)合作过程
在于胡同学接触之后,了解到他有些知识储备还不够。所以一开始大概一周时间,我先自己思考问题的分解模块、技术细节,同时给绪佩布置了任务。首先,让他读了一份代码规范,学到了不少知识;接下来,我陆陆续续地安排了他去学习了switch、随机数、栈、string类、逆波兰算法的用法,相信他有不少收获吧。
在胡同学有了一定知识储备之后,他也可以开始写一些函数了,我们开始共同编码。我们每天保持联系,沟通各自进度,咨询困惑。每天,我们远程分享桌面或者语音通话的时间都在一小时以上。
在实现了一些小函数、胡同学有了一些知识储备之后,2月7日,我在github上创建了dev分支,并开始了C++形式的编码。13日,发布了预览版。15日下午,在master分支上发布了最终版。
2)合作体会
1. 选择搭档
因为我是多有一年的学习经历,所以有很大可能我会在两个人的小队中当“主心骨”(除非说我的队友高中参加过竞赛)。
我在寻找我的搭档时,我不在意,他的水平要多高,或者打过什么代码,参加过什么竞赛之类的(在这次作业中,我自己也有很多地方是一边打,发现了问题,再去学习的)。我的搭档,他需要真正参与进来,而不是只怀着一种“抱大腿”的思想。他要愿意花时间学习,真正参与进我们两个的团体中来。他要有积极的态度,面对困难不逃避。如果能做到这些,那么就没有问题。闻道有先后,我只不过是比我的搭档提早学了一些知识而已。我的搭档只要愿意去学习,那么我们两个人一定能共同写出来一款很不错的软件,实现1+1>2。这些,我觉得胡同学做的特别好。
2. 问题模块化
不像是在OJ上面打题目,这次考虑的问题的规模就有点大了。问题一大,思考分模块处理时要十分小心。我们都知道解决一个比较大的问题时要把问题拆分,就像写一个大程序最好分块成几个函数。关于这里我有一点体会就是拆分的程度要深一点,要到足够具体的细节上。
假设一个问题可以分为A和B,B又能分为C和D。如果你在AB的层次上考虑这个问题而不是以ACD来考虑的话,那有可能在C和D上就会出毛病。如果在程序体量很庞大的情况下出现了这种失误,牵一发动全身,十分麻烦。
下面是亲身体会:原本有一个成员函数,叫RandomPart,会随机生成一个part并附加在expression后面。但在解决除号后面不能是零的问题时我又犯了难。所以我就把原本的RandomPart分解为RandomPart和AddPart两个函数。并写了新的PartValue。
3. 编码习惯的改变
这样的作业,我十分想写出“高质量”的代码,让别人都看得懂的代码——命名风格统一、语句书写清晰、编码习惯良好、注释准确到位,等等。
我觉得,我算初步实现了这一愿望。
自已写代码、自己在OJ上打题,和与他人共同编码是完全不一样的。自己写代码时,AC了就行,随便int abcde,全局变量随便开,函数接口想怎么写怎么写,不想写函数也行,全丢main里就好。但实际上这一切是不好的。变量名、全局变量、注释等编码习惯,条件判断、循环、封装等手段,不能像以前一样AC了就行,而且要让人看得懂,代码是给人看的,这样才有可维护性。
刚刚说到函数接口,这次共同编码中发现,一个函数的返回值类型和接受参数列表特别重要。假设有两个函数做的工作大致相似,但返回值类型不同、参数列表不同,这两点也足以让编码时,面对函数时的思维有很大的不同。最好在对问题有深刻理解的基础上,经过双方讨论商定后,确定函数原型,再对函数进行编码。
说点别的
不足
1. STL
如果细心的朋友可以发现,我用的是自己写的栈,而不是用标准模板库的栈。我是有打算用的,但是STL比较高级,有很多的预防性措施,而我理解的程度也不够深,导致使用了STL后,debug较为麻烦,所以就放弃了使用STL。但因此也学到了一些知识,可以看下面的参考链接。
2. git的共同协作功能
两个人都是新手,所以有的地方操作的不是那么的专业。在dev分支上开发时,我push上去的代码是保存在一个新文件里的,这样的话,就没有很好的运用github的compare(比较)功能。这是一个很棒的功能,能够显示你修改了哪些部分。如果push了一个新文件,会显示全都是“新增”。
3. “使用C++,体现面向对象的更佳”
这是一条作业加分项要求。C++用倒是用了,但个人感觉我的作业是没达到“体现面向对象”那样精髓的层面的。下面贴一个之前学C++时保存的一段笔记:
使用对象不能保证你的程序就是面向对象,初学者或者很蹩脚的程序员很可能以面向对象之虚而行面向过程之实,这样设计出来的所谓面向对象的程序很难有良好的可移植性和可扩展性。
以前不知道什么意思,写了程序才知道什么意思。纸上得来终觉浅,绝知此事要躬行!
参考链接
学习过程中对自己帮助颇大的几个网址,供大家参考!
- Expression:Deque iterator not dereferencable
- stack报错 deque iterator not dereferencable
- c++ 报错 deque iterator not incrementable
- 知乎:git 分支?
- git 创建分支提交远程分支
- 将表达式转换成逆波兰式
- STL中的stack的pop函数为什么不返回值?
- c++中string类的详解
顺带一提,本次作业使用编程环境是Microsoft Visual Studio 2015
鸣谢:Ladit
有任何意见和问题欢迎大家交流,有任何错误欢迎提出批评!