本次作业是设计并实现一个四则运算题目随机生成器(支持分数运算),其功能包括生成题目文件,并自动生成对应的答案文件,以及根据输入的题目文件,对输入的答案文件进行批改打分。
时间表格
PSP2.1 |
Personal Software Process Stages |
Time |
Planning |
计划 |
|
· Estimate |
· 估计这个任务需要多少时间 |
24h |
Development |
开发 |
|
· Analysis |
· 需求分析 (包括学习新技术) |
2h |
· Design Spec |
· 生成设计文档 |
2h |
· Design Review |
· 设计复审 (和同事审核设计文档) |
1h |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
1h |
· Design |
· 具体设计 |
2h |
· Coding |
· 具体编码 |
5h |
· Code Review |
· 代码复审 |
1h |
· Test |
· 测试(自我测试,修改代码,提交修改) |
10h |
Reporting |
报告 |
|
· Test Report |
· 测试报告 |
3h |
· Size Measurement |
· 计算工作量 |
1h |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
1h |
合计 |
29h |
从表中可以看出我的实际花费时间比预估时间多了许多,而且在编码和测试Debug上花费了巨大的时间,原因很简单,设计阶段设计考虑的不周全、不细致。
需求分析及设计方案
需求大体分为三个部分:
1.根据参数随机生成题目
2.计算出所生成题目的答案
3.对给定的题目和答案进行批改
仔细一点分析一下的话,可以得出一下几个功能需求:
1.使用命令行参数来对程序输入相关参数
2.实现分数、整数之间的四则运算
3.生成的题目保证不重复
4.生成的题目在每一步计算的时候都不会出现负数
5.输出时,所有的分数转化为真分数或者带分数
6.对读入的表达式进行分析和计算
针对上述的几点功能,很容易的想到利用一个分数类(class fraction),来统一表示题目中的数,并且重载四则运算实现计算功能以及输出为string。
fraction类定义:
class fraction { private: long long ups; long long downs; public: //构造函数 fraction(); fraction(int max); fraction(long long u, long long d); fraction(string s); //重载运算符 friend fraction operator+(const fraction &f1, const fraction &f2); friend fraction operator-(const fraction &f1, const fraction &f2); friend fraction operator*(const fraction &f1, const fraction &f2); friend fraction operator/(const fraction &f1, const fraction &f2); friend bool operator==(const fraction &f1, const fraction &f2); //toStirng转化函数 string output() const; };
对于生成的题目,我设计了problem类来进行管理,处理题目的所有需求,包括判重、计算、合法性判断以及输出为string
problem类定义:
class problem{ private: vector<char> ops; vector<fraction> numbers; public: //构造函数 problem::problem(); problem(int opn, int r); problem(vector<char> ops, vector<fraction> n); //比较和计算 bool compare(const problem &p) const; bool calculate(fraction &a) const; //toString转化函数 string output() const; };
数据结构设计好了之后,剩下的设计“似乎”就“水到渠成”了(然而这个自以为是的想法给我后面Debug,留下了好多坑)。只需要利用生成随机数,一步一步的生成题目即可。
测试
代码写完以后,我在测试上花了巨大的时间代价。。。修补之前的逻辑bug,
TestCase 1
PersonApplication.exe -n 10
TestCase 2
PersonApplication.exe -n 10 -r 10
TestCase 3
PersonApplication.exe -n 5000 -r 10
TestCase 4
PersonApplication.exe -n 10000 -r 10
TestCase 5
PersonApplication.exe -n 10000 -r 100
第二个功能的测试用例一样,但是会对答案以及题目文件进行一些改动。由于文件太大,就不展示了。
性能分析
通过分析图表可以看出,我的程序调用最频繁的是vector容器的申请和size()成员函数,是由于我在编程中经常进行压入元素和弹出元素的操作导致的。
另外compare()函数调用的也非常多,因为我的查重算法只能通过两两比较的暴力算法进行。没时间去思考和实现更好的算法了Orz......
问题、总结、收获
完成此次个人项目以下几个问题及体会。
首先是对于C++语言以及VS2012编程环境的不熟悉,导致我初期上手编码时,效率非常低,经常是一边编码,一边搜索各种小问题。
然后再编码过程中遇到的最头疼的问题是C++中的字符串处理问题。C++中,需要区分char *,Cstring,string这三种不同的字符串数据结构。由于C++对于C的继承性,有许多库函数的接口都只接受char *的参数,所以在我想要的string和各种其他类型的变量之间的转换是我花了许多时间的地方。虽然在网上查到了<sstream>中的stringstream类能很方便的构建string,但是我一开始用的方法错误......Orz....(好想念Java的string~~~)。不过我最后还是很高兴的学会了使用这个类。
最后一个问题就是不写详细设计文档的恐怖。我进行设计的时候并没有形成真正意义上的设计文档,导致我一边在思考,一边进行编码,导致后来在Debug的时候花了巨大的精力来修补之前代码中留下的各种稀奇古怪的逻辑漏洞或者笔误。不做设计累死人!!!