基于四则运算项目的结对编程项目作业记录

本次作业由郑会吉(D23181103)与刘奇(D22180806)共同完成.

仓库URL:

luiqi314/new四则运算1018

20231025补充根据题目难度打分功能,补充至4.5节。

一、基础工作

1.1  建立代码仓库,new四则运算1018并通过readme对预期的需求进行分析和规划。

图1 建立仓库_new四则运算1018

1.2  按照里程碑方式进行计划管理,初步关联5条需求,并进行了任务分配

图2 在里程碑中初步关联5条需求

1.3实际工作组成

工作内容郑会吉刘奇
制定计划根据任务要求制定计划提供设计思路
需求分析补充实用易实现的功能:限制做题时间、根据不同难度限制输出结果范围提供基础框架思路

实际开发

具体的编写程序,分别实现部分模块的功能具体的编写程序,分别实现部分模块的功能
软件测试代码审查单元测试
文档撰写工作总结博客撰写

二、需求分析

第一个版本的程序需要解决最为基础的问题,根据项目需求,从用户使用的角度确定的5个需求如下:

  1. 运算数为100 以内的数字。
  2. 需要有答题功能并验证答案是否正确。
  3. 计算正确率
  4. 避免题目重复
  5. 计算答题时间

上述是需求的自然语言描述,可能存在歧义或描述不够详细的地方,为此按GJB-438B对需求规格进行进一步分析。

功能

SZ-QU用户运行程序后,程序依次生成题目;

SZ-CORE用户通过控制台输入自己的计算结果;,控制台输出正确结果以及判断回答是否正确;

SZ-TIME完成所有题目后统计所用总时间。

性能

SZ-QU  尽量避免题目重复,回答上一题并有结果后才会生成下一题

SZ-CORE 响应时间小于10ms

SZ-TIME  时间精确到ms

可靠性暂无
可维护性为提高可测试性,开发过程中可以将功能分成不同函数单独编程,方便后续单元测试

三、初代版本程序(责任人:刘奇)

3.1  解体思路:

首先根据需求要实现可以通过随机数产生不重复的数字组合,然后在数字之间加入随机的操作符,通过比较输入的结果来打分,通过自带的时间函数统计所花时间。

在开源程序的基础上进行功能拓展及完善,开源程序URL:

// 四则运算.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//解决思路:switch进行变量控制,出版本只有加减乘除
//解决思路,0-100以内运算
//程序支持判断对错及累计得分与时间
//解决思路:对错用count计数,最终算出正确率。时间用clock函数计算
//第一版本固定出3道题目,而且不能重复(比如2 + 3 与 3 + 2 算重复的)
//采用不重复随机算法,尽量降低重复。
//想法:支持循环测验。


#include <iostream>
#include<cstdlib>
#include<time.h>
#define CLOCKS_PER_SEC ((clock_t)1000)
using namespace std;
int Difficulty(int difficulty)
{
    int N=0;
    switch (difficulty)
    {
    case 1:
        N = 100;
        break;
    case 2:
        N = 1000;
        break;
    case 3:
        N = 10000;
        break;
    default:
        cout << "请输入正确的难度。" << endl;
        break;
    }
    return N;
}
void Grade(int grade,int numberExercise,int N) {
    double a1, a2, a3,c,k, correctrate;
    double duration;//做题花费的时间
    clock_t start, finish;
int mode, count = 0;
    switch (grade)
    {
    
    case 1://*,/,1
        start = clock();
        srand((unsigned)time(NULL));
        for (int i = 0; i < numberExercise; i++) {
            a1 = rand() % N;
            a2 = rand() % N;
            mode = rand() % 4;
            switch (mode) //确定运算符
            {
            case 0:
                cout << a1 << "+" << a2 << "=" << endl;
                c = a1 + a2;
                break;
            case 1:
                c = a1 - a2;
                cout << a1 << "-" << a2 << "=" << endl;
                break;
            case 2:
                c = a1 * a2;
                cout << a1 << "*" << a2 << "=" << endl;
                break;
            case 3:
                c = a1 / a2;
                cout << a1 << "/" << a2 << "=" << endl;
                break;
            }
            cout << "请输入答案:";
            cin >> k;
            if (k == c) {
                cout << "恭喜你答对了,请再接再厉" << endl;
                count = count + 1;
            }
            else if (k != c) {
                cout << "虽然答错了,但不要灰心哦,正确答案是" << c << endl;
            }
        }
        correctrate = (count * 100) / numberExercise;
        finish = clock();
        duration = (double)(finish - start) / CLOCKS_PER_SEC;
        cout << "恭喜你做完了" << numberExercise << "道题目,做对了" << count << "道题目,正确率为 " << correctrate << "%" << "  一共耗时(s):" << duration << endl;
        break;

    }
    
}
int main()
{
    int T=0;
    do {
        int grade, numberExercise, difficulty, N = 0;
        grade = 1;
        difficulty = 1;
        numberExercise = 3;
        N = Difficulty(difficulty);
        Grade(grade, numberExercise, N);
        T++;
    } while (T==0);
    system("pause");
    return 0;
}

在VS2022中进行程序编写及测试:

3.2  代码审查:

第一版程序IDE代码审查结果:

运行code_analyse对代码质量进行分析

warning C4101: “a3”: 未引用的局部变量

warning C26451: 算术溢出: 使用 4 字节值上的运算符 - ,然后将结果转换到 8 字节值。在调用运算符 - 之前将值强制转换为宽类型可避免溢出(io.2)。

运算符 - 之前将值强制转换为宽类型可避免溢出(io.2)。

图3 解决警告

两个waring均解决。

采纳vs推荐的修改方案。

3.3  性能分析

在VS2022的性能探查器得到的结果如下图:

图4 性能分析结果

从DPU的使用量看,函数中主要是外部调用的

图5 语句分析

在控制台输出cout以及比较最终结果是否正确花费了不少时间。为此将system(pause)注释掉,不需要系统的等待时间。

图6 性能变化

初始程序的性能探查,修改后的性能分析如下图:

图7 性能变化

四、版本迭代

4.1增加需求:实现-增加题目难度、运算数个数的选择功能(责任人:郑会吉)

代码如下:

// 四则运算.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//分别能够实现小学一、二、三、四、五年级的四则运算要求, 逐步实现各个年级的难度 
//解决思路:switch进行变量控制,年纪用+,-,*,/,运算个数来分类。一年级只有+,-,二年级三个数进行+,-,三年级*,/,四年级三个数运算,五年级小数运算。()加入其中。
//要求能够通过输入来选择不同年级,每个年级还得区分难,中,易三个等级 
//解决思路,易0-100以内运算,中,100-1000以内运算,难,1000-10000以上运算
//程序支持判断对错及累计得分与时间
//解决思路:对错用count计数,最终算出正确率。时间用clock函数计算
//一次可以出100道题目,而且不能重复(比如2 + 3 与 3 + 2 算重复的)
//采用不重复随机算法,尽量降低重复。
//充分发挥想象增加满足小学生数学检测需要的功能 
//想法:支持循环测验。


#include <iostream>
#include<cstdlib>
#include<time.h>
#define CLOCKS_PER_SEC ((clock_t)1000)
using namespace std;
int Difficulty(int difficulty)
{
    int N=0;
    switch (difficulty)
    {
    case 1:
        N = 100;
        break;
    case 2:
        N = 1000;
        break;
    case 3:
        N = 10000;
        break;
    default:
        cout << "请输入正确的难度。" << endl;
        break;
    }
    return N;
}
void Grade(int grade,int numberExercise,int N) {
    double a1, a2, a3,c,k, correctrate;
    double duration;//做题花费的时间
    clock_t start, finish;
int mode, count = 0;
    switch (grade)
    {
    case 1: //+,-,2
        start = clock();
        srand((unsigned)time(NULL));
        for (int i = 0; i < numberExercise; i++) {
            a1 = rand() % N;
            a2 = rand() % N;
            mode = rand() % 2;
            switch (mode) //确定运算符
            {
            case 0:
                cout << a1<< "+" << a2 << "=" << endl;
                c = a1+a2;
                break;
            case 1:
                c = a1-a2;
                cout << a1 << "-" << a2<< "=" << endl;
                break;
            }
            cout << "请输入答案:";
            cin >> k;
            if (k == c) {
                cout << "恭喜你答对了,请再接再厉" << endl;
                count = count + 1;
            }
            else if (k != c) {
                cout << "虽然答错了,但不要灰心哦,正确答案是" << c << endl;
            }
        }
        correctrate = (count*100) / numberExercise;
        finish = clock();
        duration = (double)(finish - start) / CLOCKS_PER_SEC;
        cout << "恭喜你做完了" << numberExercise << "道题目,做对了" << count << "道题目,正确率为 " << correctrate << "%" <<"  一共耗时(s):"<<duration<<endl;
        break;
    case 2://+-,3
        start = clock();
        srand((unsigned)time(NULL));
        for (int i = 0; i < numberExercise; i++) {
            a1 = rand() % N;
            a2 = rand() % N;
            a3 = rand() % (2*N)-N;
            mode = rand() % 2;
            switch (mode) //确定运算符
            {
            case 0:
                cout << a1 << "+" << a2 <<"+("<<a3<< ")=" << endl;
                c = a1+a2+a3;
                break;
            case 1:
                c = a1 - a2+a3;
                cout << a1 << "-" << a2 <<"+("<<a3<< ")=" << endl;
                break;
            }
            cout << "请输入答案:";
            cin >> k;
            if (k == c) {
                cout << "恭喜你答对了,请再接再厉" << endl;
                count = count + 1;
            }
            else if (k != c) {
                cout << "虽然答错了,但不要灰心哦,正确答案是" << c << endl;
            }
        }
        correctrate = (count * 100) / numberExercise;
        finish = clock();
        duration = (double)(finish - start) / CLOCKS_PER_SEC;
        cout << "恭喜你做完了" << numberExercise << "道题目,做对了" << count << "道题目,正确率为 " << correctrate << "%" << "  一共耗时(s):" << duration << endl;
        break;
    case 3://*,/,2
        start = clock();
        srand((unsigned)time(NULL));
        for (int i = 0; i < numberExercise; i++) {
            a1 = rand() % N;
            a2 = rand() % N;
            mode = rand() % 4;
            switch (mode) //确定运算符
            {
            case 0:
                cout << a1 << "+" << a2 << "=" << endl;
                c = a1 + a2;
                break;
            case 1:
                c = a1 - a2;
                cout << a1 << "-" << a2 << "=" << endl;
                break;
            case 2:
                c = a1 * a2;
                cout << a1 << "*" << a2 << "=" << endl;
                break;
            case 3:
                c = a1 / a2;
                cout << a1 << "/" << a2 << "=" << endl;
                break;
            }
            cout << "请输入答案:";
            cin >> k;
            if (k == c) {
                cout << "恭喜你答对了,请再接再厉" << endl;
                count = count + 1;
            }
            else if (k != c) {
                cout << "虽然答错了,但不要灰心哦,正确答案是" << c << endl;
            }
        }
        correctrate = (count * 100) / numberExercise;
        finish = clock();
        duration = (double)(finish - start) / CLOCKS_PER_SEC;
        cout << "恭喜你做完了" << numberExercise << "道题目,做对了" << count << "道题目,正确率为 " << correctrate << "%" << "  一共耗时(s):" << duration << endl;
        break;
    case 4://+,-,*,/,3
        start = clock();
        srand((unsigned)time(NULL));
        for (int i = 0; i < numberExercise; i++) {
            a1 = rand() % N;
            a2 = rand() % N;
            a3 = rand() % (2 * N) - N;
            mode = rand() % 4;
            switch (mode) //确定运算符
            {
            case 0:
                cout << a1 << "+(" << a2 <<"*("<<a3<<"))=" << endl;
                c = a1 + a2*a3;
                break;
            case 1:
                c = a1 - a2/a3;
                cout << a1 << "-(" << a2 << "/(" << a3 << "))=" << endl;
                break;
            case 2:
                c = a1 * (a2-a3);
                cout << a1 << "*(" << a2 << "-(" << a3 << "))=" << endl;
                break;
            case 3:
                c = a1 / (a2+a3);
                cout << a1 << "/(" << a2 << "+(" << a3 << "))=" << endl;
                break;
            }
            cout << "请输入答案:";
            cin >> k;
            if (k == c) {
                cout << "恭喜你答对了,请再接再厉" << endl;
                count = count + 1;
            }
            else if (k != c) {
                cout << "虽然答错了,但不要灰心哦,正确答案是" << c << endl;
            }
        }
        correctrate = (count * 100) / numberExercise;
        finish = clock();
        duration = (double)(finish - start) / CLOCKS_PER_SEC;
        cout << "恭喜你做完了" << numberExercise << "道题目,做对了" << count << "道题目,正确率为 " << correctrate << "%" << "  一共耗时(s):" << duration << endl;
        break;
    case 5://小数运算
        start = clock();
        srand((unsigned)time(NULL));
        for (int i = 0; i < numberExercise; i++) {
            a1 = rand() % N+ rand() / double(RAND_MAX);//加上0-1的小数,即可。
            a2 = rand() % N+ rand() / double(RAND_MAX);
            a3 = rand() % (2 * N) - N+ rand() / double(RAND_MAX);
            mode = rand() % 4;
            switch (mode) //确定运算符
            {
            case 0:
                cout << a1 << "+(" << a2 << "*(" << a3 << "))=" << endl;
                c = a1 + a2 * a3;
                break;
            case 1:
                c = a1 - a2 / a3;
                cout << a1 << "-(" << a2 << "/(" << a3 << "))=" << endl;
                break;
            case 2:
                c = a1 * (a2 - a3);
                cout << a1 << "*(" << a2 << "-(" << a3 << "))=" << endl;
                break;
            case 3:
                c = a1 / (a2 + a3);
                cout << a1 << "/(" << a2 << "+(" << a3 << "))=" << endl;
                break;
            }
            cout << "请输入答案:";
            cin >> k;
            if (k == c) {
                cout << "恭喜你答对了,请再接再厉" << endl;
                count = count + 1;
            }
            else if (k != c) {
                cout << "虽然答错了,但不要灰心哦,正确答案是" << c << endl;
            }
        }
        correctrate = (count * 100) / numberExercise;
        finish = clock();
        duration = (double)(finish - start) / CLOCKS_PER_SEC;
        cout << "恭喜你做完了" << numberExercise << "道题目,做对了" << count << "道题目,正确率为 " << correctrate << "%" << "  一共耗时(s):" << duration << endl;
        break;
    default:
        cout << "请输入正确的年级。" << endl;
        break;
    }
}
int main()
{
    int T;
    do {
        int grade, numberExercise, difficulty, N = 0;
        cout << "请选择您的当前年级:1:一年级,2:二年级,3:三年级,4:四年级,5:五年级。" << endl;
        cin >> grade;
        cout << "请选择难度:1:简单,2:普通,3:困难。" << endl;
        cin >> difficulty;
        cout << "请输入你要做的题数:" << endl;
        cin >> numberExercise;
        N = Difficulty(difficulty);
        Grade(grade, numberExercise, N);
        cout << "如果您想继续做题,请输入1,退出系统请输入其他任意数" << endl;
        cin >> T;
    } while (T==1);
    system("pause");
    return 0;
}

修改后的代码能够根据输入的年级数确定计算的难度,并根据输入的数确定题目的数量,并限制计算数的大小。

通过fork的分支修改后进行pull request后,由主分支的责任人在Giteediamante审查通过后合并进主分支:

Gitee代码审查:

4.2增加需求:增加防沉迷功能(责任人:刘奇)

考虑到小学123年级比较低,可能在电脑上使用该软件的时候时间过长导致近视眼,在选择年级为123的时候,每道题结束后都会显示目前为止所花的时间,当时间达到设定值的时候会提示并结束本次答题。

如上图所示,设定时间限制5000ms,输入3题,但实际完成两题后时间超过5000ms,此时控制台显示用时过长,休息一下吧!

4.3增加需求:优化题目难度(责任人:郑会吉)

对于小学一年级的题目,只有加减操作,但可能会出现结果为负数的情况,考虑到小学可能没有学习负数相关知识。增加新的需求,对题目难度进行优化,对于小学一年级的学生,不应该出现计算结果为负数的题,因此判断减法中如果出现a-b且a小于b的情况,就将a、b对调,从而保证减法运算的结果一定为正数。修改部分代码如下所示:        

  switch (mode) //确定运算符
  {
  case 0:
      c = a1 + a2;
      cout << a1 << "+" << a2 << "=" << endl;
    //  return "";
      break;
  case 1:
     
      if (a1 < a2)
      {
          int b1=0;
          b1 = a1;
          a1 = a2;
          a2 = b1;
          c = a1 - a2;

      }
      cout << a1 << "-" << a2 << "=" << endl;
      break;
  }

4.4增加需求:限制输出结果(责任人:刘奇)

为限制一年级计算的输出结果,在任何情况下计算的结果都不会超过对应难度的范围:例如100以内的加减不超过100,1000以内的加减不超过1000.且减法以及得到了限制,通过在加法中根据第一个生成的数限制第二个数的rand范围来实现。修改主要代码部分如下所示:

  int N2=1;
  a1 = rand() % N;
  N2 = N - a1;
  a2 = rand() % N2;
  mode = rand() % 2;

4.5补充根据题目难度打分功能(责任人:郑会吉)

通过判断计算数的大小范围,100以内2分,1000以内5分,10000以内8分,由此得到卷面总分,然后计算回答正确的题目的分数,由此计算得分的比例*100%。

相关代码如下:
 if (k == c) {
                cout << "恭喜你答对了,请再接再厉" << endl;
                count = count + 1;
                if (N == 100) {
                    score = score + 5;
                }
                else if (N == 1000) {
                    score = score + 8;
                }
                else {
                    score = score + 10;
                }
                
            }
            else if (k != c) {
                cout << "虽然答错了,但不要灰心哦,正确答案是" << c << endl;
            }
            now_time = clock() - start;
            cout << "当前总用时" << now_time << endl;
            if (now_time > 50000)
            {
                cout << "用时过长,休息一下吧!" << endl;
                break;
            }
        }
        correctrate = (count * 100) / numberExercise;
        finish = clock();
        duration = (double)(finish - start) / CLOCKS_PER_SEC;
        cout << "恭喜你做完了" << numberExercise << "道题目,做对了" << count << "道题目,正确率为 " << correctrate << "%,得分为" << score/score_all*100 <<"  一共耗时(s):" << duration << endl;
        break;

 五、软件测试:

5.1 测试分析

测试前首先对测试用例进行分析:

编号被测模块测试功能测试用例期望
DIF-1Difficulty函数正常输入时的输出输入 1100
DIF-2Difficulty函数边界输入的输出输入 310000
DIF-3Difficulty函数错误输入的输出输入 50
GRA-1Grade函数低年级是否有时长限制输入(1,1,5)模拟时长6000 "用时过长,休息一下吧!"
GRA-2Grade函数1年级是否不会输出结果为负数的题输入(1,1,)模拟随机数2-100输出交换两数位置
GRA-3Grade函数1年级是否不会输出结果超过操作数大小的题输入(1,1,20)20个题结果均在100以内
GRA-4Grade函数输入6年级边界测试输入(6,3,100)
GRA-5Grade函数输入1年级边界测试输入(1,1,100)
GRA-6Grade函数4年级是不是4个操作数输入(4,1,100)检查100个题目满足
GRA-7Grade函数题目是不是不会重复输入(1,1,100)检查100个题目不重复

5.2测试代码

如下:

#include "pch.h"
#include "CppUnitTest.h"
#include "../main/四则运算.cpp"

using namespace Microsoft::VisualStudio::CppUnitTestFramework;



class MockInput {
public:
	MockInput(int inputValue) : m_inputValue(inputValue) {}

	int getInput() {
		return m_inputValue;
	}

private:
	int m_inputValue;
};

namespace 四则test
{
	TEST_CLASS(四则test)
	{
	public:
		int expected1 = 100; 
		
		TEST_METHOD(TestMethod1)
		{
			int  difficulty = 1;

			int number_max = Difficulty(difficulty);

			Assert::AreEqual(expected1, number_max);
		}

		TEST_METHOD(TestMethod2)
		{
			int  difficulty = 4;
			int expected2 = 0;
			std::string err_diff = "请输入正确的难度。";

			//string warn_err_diff = Difficulty(difficulty);
			int number_max = Difficulty(difficulty);

			Assert::AreEqual(expected2, number_max);
		}

		/*TEST_METHOD(std_test)
		{
			int  difficulty = 4;
			int expected2 = 0;
			std::string err_diff = "请输入正确的难度。";

			//string warn_err_diff = Difficulty(difficulty);
			int number_max = Difficulty(difficulty);
			std::stringstream buffer;
			std::streambuf* sbuf = std::cout.rdbuf(); // Save cout's buffer
			std::cout.rdbuf(sbuf);
			std::cout << "std original buffer: \n";
			std::cout << buffer.get();

			Assert::AreEqual(err_diff, buffer.str());
		}*/

		TEST_METHOD(Testgrade1)
		{
			int  grade = 1;
			int  test_num = 1;
			int  max_num = 100;


			int expected2 = 0;

			std::string expected = "Hello World!\n";

			//string warn_err_diff = Difficulty(difficulty);
		    Grade(grade, test_num, max_num);

			MockInput input(5);
			Grade(input.getInput());

			std::stringstream buffer;
			std::streambuf* sbuf = std::cout.rdbuf(); // Save cout's buffer
			std::cout.rdbuf(sbuf);
			std::cout << "std original buffer: \n";
			std::cout << buffer.get();


			Assert::AreEqual(expected, buffer.str());
		}
	};
}

目前仅对Difficulty函数进行了测试,Grade函数返回值为空,且需要模拟控制台输入,比较复杂,目前测试未通过;(有待下一步深入学习)

六、学习记录

6.1 每日学习日志

学习时段

学习内容

收获体会

效率评价

1016

2000-2200

需求分析

要明确需求的谁对谁的需求,不要把需求写成了技术实现

1017

1400-1800

编写初代代码

从开源代码中学习了不少好的思路

1018

1400-1730

初代代码运行与效能分析

效能分析的结果许多不一定是语言的问题,而是与系统执行有关

良好

1019

1400-1800

Gitee高级操作

协同编程的时候,通过更新自己的分支到最新再pullreqeuest否则会冲突

一般

1020

0800-1400

软件测试,返回值为void的函数如何进行测试,需要交互后运行的函数如何通过mock来模拟控制台输入

软件=程序+数据+文档

测试的目的:需求规格说明与实际结果是否一致

一般

1021

学习时间学习内容收获体会自我效率评价
2023/10/12计划和预估两人商量确定了项目的计划和流程基本达到预期目标
1400-1600
2023/10/13需求分析找相关资料,完成开发前的准备工作基本达到预期目标
0800-1100
2023/10/13开发编码,实现题目的生成未达到预期目标
1400-1700
1900-2300
2023/10/14开发编码,实现题目的生成、答题功能并验证答案是否正确、判分,并对历史成绩进行存储和查询。 题目避免重复基本达到预期目标
0800-1100
1400-1700
1900-2300
2023/10/18开发和测试代码复审、单元测试基本达到预期目标
1400-1700
1900-2300
2023/10/19测试和总结 文档撰写基本达到预期目标
1400-1700
1900-2300

6.2 PSP效率分析:

根据个人软件开发的几个主要步骤,给出了预估耗时以及实际耗时的对比,其中代码以及测试方面所花时间远超过预估,主要是因为调试不是很熟练,花的时间比较久。整体而言,整个开发流程的实际耗时比预估多了69%,一方面是经验不足,一方面也是时间比较碎,每次进行相关工作需要先进入状态才能开始高效率的工作。

个人软件开发阶段

预估耗时

实际耗时

计划

2h

2h

需求分析,生成设计文档

1h

2h

代码规范审查

1h

1h

具体编码

5h

10h

测试

2h

6h

测试报告

3h

1h

总结

2h

5h

合计

16h

27h

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值