四则运算生成器

本次个人项目为实现一个随机生成四则运算算式的程序,同时提供检查答案的功能。整个项目的情况如下。

一、总体项目PSP表格

PSP2.1

Personal Software Process Stages

Time

Planning

计划

8小时

  · Estimate

  · 估计这个任务需要多少时间

8小时 

Development

开发

15小时20分钟 

  · Analysis

  · 需求分析 (包括学习新技术)

40分钟

  · Design Spec

  · 生成设计文档

30分钟 

  · Design Review

  · 设计复审 (和同事审核设计文档)

0

  · Coding Standard

  · 代码规范 (为目前的开发制定合适的规范)

10分钟

  · Design

  · 具体设计

1小时

  · Coding

  · 具体编码

6小时

  · Code Review

  · 代码复审

0

  · Test

  · 测试(自我测试,修改代码,提交修改)

8小时

Reporting

报告

 

  · Test Report

  · 测试报告

0

  · Size Measurement

  · 计算工作量

30分钟

  · Postmortem & Process Improvement Plan

  · 事后总结并提出过程改进计划

30分钟

 

合计

17小时20分钟

代码实现过程中,由于对设计复审、代码复审以及测试报告的具体作用的不理解,未能进行这几个过程。

二、需求分析

这里仿照《一线架构师实践指南》中给出的ADMEMS矩阵(需求层次——需求方面矩阵)方法对个人项目进行需求分析。由于这里没有运维等业务级需求,因此将原方法进行了简化。在此,我将原题目中的需求划分为了用户级和开发级。其中,用户级需求是从用户角度看,满足所需要实现的种种功能。而开发级需求则是在开发的过程中,我们所需要考虑的开发层面的问题。同时,我们将每一级的需求划分为三类,功能性需求、质量要求以及约束条件。

将原题目中给出的所有需求按照这些分类整理在下面的表格中。我们可以根据用户的“支持10000道题目生成”进一步推出一个原需求中未提及的开发质量需求:整个程序的算法复杂度在O(n)到O(nlogn)左右,否则无法在相对合理的时间内生成一万道题目。

  功能 质量 约束
用户级需求

命令行参数输入出题数、数据范围

自动生成四则运算,支持整数和真分数

支持括号

题目存入Exercises.txt中

同时生成题目答案,存入Answers.txt中

可以判断答案对错

支持一万道题目的生成

用户输入有误时程序不崩溃

错误输入时需输出帮助信息

生成的算式需满足:

  减法计算无负数

  除法计算结果为真分数

  运算符不超过3个

  +和*交换不重复

真分数五分之三表示为3/5

真分数二又八分之三表示为2’3/8

Exercises.txt文件格式如下:

1. 四则运算题目1

2. 四则运算题目2

……

Answers.txt文件格式如下:

1. 答案1

2. 答案2

判卷结果输出到文件Grade.txt,格式如下:

Correct: 5 (1, 3, 5, 7, 9)

Wrong: 5 (2, 4, 6, 8, 10)

开发级需求

写出至少10个测试用例确保你的程序能够正确处理各种情况。

算法复杂度在O(n)到O(nlogn)左右

经过Code Quality Analysis工具的分析并消除所有的警告

采用C++或者C#语言实现,可以使用.Net Framework

运行环境为32-bit Windows 78

三、项目设计

(1) 概要设计

分数类:用于处理分数之间的计算以及输入输出,是整个程序中数的最基本单位。

 1 class Fraction
 2 {
 3 public:
 4     Fraction(); 5     Fraction(const Integer& a, const Integer& b = 1);
 6     ~Fraction(void);
 7 
 8     Fraction operator + (const Fraction&) const;
 9     Fraction operator - (const Fraction&) const;
10     Fraction operator * (const Fraction&) const;
11     Fraction operator / (const Fraction&) const;
12 
13     bool operator > (const Fraction&) const;
14     bool operator < (const Fraction&) const;
15     bool operator == (const Fraction&) const;
16 
17     bool valid();18     Integer getNumerator() const;19     Integer getDenominator() const;20 
21     friend std::wostream& operator << (std::wostream& out, const Fraction& frac);
22     friend std::wistream& operator >> (std::wistream& in, Fraction& frac);
23 };
24     

算式类:在分数类的基础上,提供和算式相关的功能,包括输入输出计算答案,比较两个算式是否相等。

 1 class Equation
 2 {
 3 public:
 4     Equation(); 5     Equation(const Equation& eq);
 6     Equation(Fraction *n); 7     Equation(Equation* l, Equation* r, wchar_t flag);
 8     ~Equation(void);
 9 
10     const Fraction& getAns() const;11     bool valid();12 
13     bool operator == (const Equation& eq) const;
14     friend std::wostream& operator << (std::wostream& out, const Equation& eq);
15     friend std::wistream& operator >> (std::wistream& in, Equation& eq);
16 };

用户界面模块:解析命令行参数输入,负责与用户相关的交互等。

随机出题模块:负责不重复地生成一定数量的题目。

(2)具体设计

在上述的设计中,Equation类的内部需要单独进行一些设计。因为,其涉及到如何判断两个等式是否相等、如何计算答案的问题。

经过思考,我最终采用了树状结构来表示整个表达式。我按照计算顺序将其构建为一棵树,其中每个叶节点为算式中的数,而内部节点为符号。

四、测试用例

(1)整体测试

case 1:

直接运行Myapp.exe,测试无参数状态下能否正常输出提示。

case 2:

Myapp.exe -n 10,测试无-r是否输出提示。

case 3:

Myapp.exe -n -1 -r 10

Myapp.exe -n 10 -r -1 

Myapp.exe -n 2t -r 10

Myapp.exe -n 10 -r ttt

测试-n和-r参数有误时能否正确输出提示。

case 4:

Myapp.exe -n 10 -r 10

测试正确输入下,能否按照需求正确生成不重复的式子。

case 5:

Myapp.exe -n 10000 -r 10

测试正确的大数据量输入下,能否按照需求正确生成不重复的式子。

case 6:

Myapp.exe -e Exercises.txt -a Answers.txt

测试正确输入下能否正确进行判卷工作。

case 7:

修改之前的Answer.txt,将部分答案改成错误的。

Myapp.exe -e Exercises.txt -a Answers.txt

测试正确输入下能否正确进行判卷工作。

case 8:

修改之前的Answers.txt,将部分答案改成错误的格式。

Myapp.exe -e Exercises.txt -a Answers.txt

测试错误输入下能否正确输出相应的提示。

case 9:

生成10000个的同时进行要求程序判断Exercises.txt和Answers.txt中的答案是否正确。

Myapp.exe -n 10000 -r 10 -e Exercises.txt -a Answers.txt

多次运行,确保每次的Grade.txt中的结果均为正确10000道。

(2)单元测试

在开发的过程中,我首先进行了单元测试,以保证关键模块的正确性。

对于Fraction类,我进行了以下几组测试:

  1. 检验构造函数及化简是否正确,比对(i*j)/j和i是否相等(i,j在1-100之间枚举),若相等则说明Fraction的构造函数及相关的自动化简等功能正确。
  2. 枚举所有i/j,其中i,j在100的范围内,将该分数写入到文件,再从文件读出,若二者相等,则说明Fraction类对于输入输出流的重载正确。
  3. 测试文件中的数据若是格式错误的,能否正确设置流的badbit。

对于Equation类,我进行了以下几组测试:

  1. 测试Equation类相等的实现是否正确。采用了老师在个人项目的描述中所使用的几个例子。
  2. 同Fraction类一样,测试了输入输出流相关重载是否正确。(共选用了4组较为具有代表性的用例)

由于这两个类十分核心,因此,这两个类的单元测试通过,基本可以确保程序整体上是正确的。

根据VS给出的代码覆盖率分析,我进行的测试可以覆盖70%以上的代码,余下的部分主要是拷贝构造函数、getter/setter、分数的四则运算等极其不易出错的短代码。

而位于核心部分的输入输出流的解析等都覆盖得相对较为完整。

 五、性能分析及改进

整个程序常调用的一个路径是维护分数的最简的代码。同时VS的分析也指出了,去重部分占据了相当大的一段时间。我的去重是采用set完成的。set内部的红黑树也许是一个值得改进的地方。

红黑树的算法复杂度相较于哈希表较高,可以采用C++11标准中的unordered_set代替set,以改进性能。

原文地址:https://www.cnblogs.com/fzyz999/p/4822324.html