软件工程(2019)结对编程第二次作业

1 题目要求

我们在刚开始上课的时候介绍过一个小学四则运算自动生成程序的例子,请实现它,要求:

  • 能够自动生成四则运算练习题
  • 可以定制题目数量
  • 用户可以选择运算符
  • 用户设置最大数(如十以内、百以内等)
  • 用户选择是否有括号、是否有小数
  • 用户选择输出方式(如输出到文件、打印机等)
  • 最好能提供图形用户界面(根据自己能力选做,以完成上述功能为主)

2 任务分工

  • 驾驶员 :姚双合 Coding地址
  • 领航员 :孙治朝 博客地址

3 开发

3.1对象定义

开发使用的语言是C++,面向对象编程,定义了一个类Calculator,具体定义如下:

class Calculator
{
/*Op存储的是运算符
Parenthesis控制是否有括号
Decimal控制是否有小数
MaxNum存储的是最大范围值
Num存储的是参与运算的数量*/
public:
    
    string Op = "";
    bool Parenthesis;
    bool Decimal;
    bool IsFile;
    int MaxNum;
    int Num;
public:
    //构造函数
    Calculator()
    {
        Parenthesis = false;
        Decimal = false;
        MaxNum = 100;
        Num = 5;
        IsFile = false;
    }
    //这几个函数用于判断输入数据是否非法,正确即返回true,非法即返回false
    bool IsRight_Parenthesis(char P_temp);
    bool IsRight_Decimal(char D_temp);
    bool IsRight_Num(int MaxNum_temp);
    bool IsRight_MaxNum(int MaxNum_temp);
    bool IsRight_File(char F_temp);
    //生成算式
    string MakeFormula();
    string MakeFormula_f();
};

3.2 对象实现

对浮点数的处理

随机生成的浮点数,末尾会自动补0,所以需要对浮点数处理,保留小数点后两位。具体代码如下:

//除去浮点数多余的位数
static std::string RoundAny(float r, int precision) {
    std::stringstream buffer;
    buffer << std::fixed << std::setprecision(precision) << r;
    return buffer.str();
}

不含小数的算式

//生成整数算式
string Calculator::MakeFormula() 
{
    int Left = 0;//左括号位置
    int Right = 0;//右括号位置
    string formula = "";//用于存放生成的算式
    int Len = Op.length();//运算符个数
    int Operation = 0;
    int Count = random(2, Num);//参与运算的数字的个数
    int Start = 1;//循环控制变量
    int Tmp=0;
    int number1 = random(1, MaxNum);//生成随机数
    int number2 = 0;
    //只有两个数运算时不加括号
    if(Count!=2&&Parenthesis==true)
    {
        Left = random(1, Count-1 );
        Tmp = Left + 1;//暂时存放Left+1
        if (Left + 1 == Count)//左括号在倒数第二个数的位置时,指定右括号为最后一个数的位置
        {
            Right = Count;
        }
        else
        {
            Right = random(Tmp, Count );
        }
        while (Left == 1 && Right == Count )//排除左括号在第一个位置,右括号在最后一个位置
        {
            Right = random(Tmp, Count );
        }
    }
    //判断第一个数要不要加括号
    if (Left == 1&&Count!=2)
    {
        formula += '(' + to_string(number1);
    }
    else
    {
        formula += to_string(number1);
    }
    //生成的格式是“运算符+随机数”
    while (Start < Count)
    {
        Start++;
        switch (Len)//随机从运算符中选择一个
        {
        case 1:Operation = 0; break;
        case 2:Operation = random(0, 1); break;
        case 3:Operation = random(0, 2); break;
        case 4:Operation = random(0, 3); break;
        }
        number2 = random(1, MaxNum);//生成随机数
        if (Start == Left && Parenthesis == true)//左括号位置
        {
            formula =formula+ Op[Operation] +'('+ to_string(number2);
        }
        else if (Start == Right && Parenthesis == true)//右括号位置
        {
            formula =formula +Op[Operation] + to_string(number2)+')';
        }
        else//不加括号
        {
            formula += Op[Operation] + to_string(number2);
        }   
    }
    formula += '=';
    return formula;
}

含小数的算式

该函数的实现方法与不含小数算式的实现方法一样,唯一不同的地方在于,

  • 整数算式中,随机生成的正数,是由random(a,b)宏定义实现的,具体定义在代码第9行#define random(a,b) (rand()%(b-a+1)+a)
  • 小数算式中,随机生成的小数,是由rand() % MaxNum + 1 + (float)(rand() % MaxNum) / MaxNum实现的
    其余部分则完全一样。

判断参数是否传入

对于Parenthesis Decimal IsFile这三个参数,设计了具体函数用来判断输入的数据是否合法,以Decimal为例,具体代码实现如下:

//判断Decimal参数是否传入,成功返回真,失败返回假
bool Calculator::IsRight_Decimal(char D_temp)
{
    if ((D_temp == 'Y') || (D_temp == 'y'))
    {
        Decimal = true;
        return true;
    }
    else if ((D_temp == 'N') || (D_temp == 'n'))
    {
        Decimal = false;
        return true;
    }
    else
    {
        return false;
    }
}

MaxNum参数与Num参数与之类似,如果参数成功输入,返回真,否者返回假,以Num为例,具体代码实现如下:

//判断Num参数是否传入,成功返回真,失败返回假
bool Calculator::IsRight_Num(int Num_temp)
{
    if (Num_temp > 0)
    {
        Num = Num_temp;
        return true;
    }
    else
    {
        return false;
    }
}

3.3 主函数

由于个人能力有限,短时间内无法写出用户图形界面,所以主函数中使用控制台进行输入。主函数中存在错误处理,如果输入数据非法的话,会提示重新输入。同时会根据输入的数据进行判断,调用哪些函数来输出算式。具体代码如下:

/*主函数中变量的含义
Calc是生成随机算式的类
Temp存放的是运算符
P_temp存放的是输入的是否有括号的字符
D_temp存放的是是否有小数的字符
Quantity存放的是生成题目的数量
MaxNum_temp存放的是参与运算的最大值
*/
int main()
{
    Calculator* Calc = new Calculator();
    char Temp = NULL;
    char P_temp = NULL;
    char D_temp = NULL;
    char F_temp = NULL;
    int Quantity = 0;
    int MaxNum_temp = 100;
    int Num_temp = 5;

    cout << "请输入您期望生成的题目数量:" << endl;
    cin >> Quantity;
    cout << "您希望参与运算的最大数是多少?(默认100以内)" << endl;
    cin >> MaxNum_temp;
    while (!Calc->IsRight_MaxNum(MaxNum_temp))
    {
        cout << "非法!请输入正确的范围!" << endl;
        cout << "您希望参与运算的最大数是多少?(默认100以内)" << endl;
        cin >> MaxNum_temp;
    }
    cout << "您希望参与运算的数的最大数量是多少?(默认5个数)" << endl;
    cin >> Num_temp;
    while (!Calc->IsRight_Num(Num_temp))
    {
        cout << "非法!请输入正确的范围!" << endl;
        cout << "您希望参与运算的数的最大数量是多少?(默认5个数)" << endl;
        cin >> Num_temp;
    }
    cout << "请输入您期望参与运算的运算符:" << endl;
    cin >> Temp;
    while (Temp != '\n')
    {
        if (Temp != ' ')
        {
            if (Temp == '+')
                Calc->Op += "+";
            if (Temp == '-')
                Calc->Op += "-";
            if (Temp == '*')
                Calc->Op += "*";
            if (Temp == '/')
                Calc->Op += "/";
        }
        Temp = cin.get();
    }
    cout << "您希望算式中有括号吗?(输入Y/N,不分大小写)" << endl;
    cin >> P_temp;
    while (!Calc->IsRight_Parenthesis(P_temp))
    {
        cout << "非法!请您输入正确的字符!" << endl;
        cout << "您希望算式中有括号吗?(输入Y/N,不分大小写)" << endl;
        cin >> P_temp;
    }
    cout << "您希望算式中有小数吗?(输入Y/N,不分大小写)" << endl;
    cin >> D_temp;
    while (!Calc->IsRight_Decimal(D_temp))
    {
        cout << "非法!请您输入正确的字符!" << endl;
        cout << "您希望算式中有小数吗?(输入Y/N,不分大小写)" << endl;
        cin >> D_temp;
    }
    cout << "您希望把算式保存至文档吗?(输入Y/N,不分大小写)" << endl;
    cin >> F_temp;
    while (!Calc->IsRight_File(F_temp))
    {
        cout << "非法!请您输入正确的字符!" << endl;
        cout << "您希望把算式保存至文档吗?(输入Y/N,不分大小写)" << endl;
        cin >> F_temp;
    }
    //创建文件
    ofstream OutFile("Formula.txt", ios::out | ios::binary);
    if (!OutFile)
    {
        cout << "创建文件失败!" << endl;
    }
    srand((unsigned int)time(NULL));
    while (Calc->Parenthesis)
    {
        if (Calc->Decimal)
            //有括号有小数
        {
            while (Quantity != 0)
            {
                string Question = Calc->MakeFormula_f();
                cout << Question << endl;
                if (Calc->IsFile)
                {
                    for (int i = 0; i < Question.size(); i++)
                    {
                        OutFile << Question[i];
                    }
                    OutFile << endl;
                }
                Quantity--;
            }
        }
        else
            //有括号没小数
        {
            while (Quantity != 0)
            {
                string Question = Calc->MakeFormula();
                cout << Question << endl;
                if (Calc->IsFile)
                {
                    for (int i = 0; i < Question.size(); i++)
                    {
                        OutFile << Question[i];
                    }
                    OutFile << endl;
                }
                Quantity--;
            }
        }
    }
    while (!Calc->Parenthesis)
    {
        if (Calc->Decimal)
            //没括号有小数
        {
            while (Quantity != 0)
            {
                string Question = Calc->MakeFormula_f();
                cout << Question << endl;
                if (Calc->IsFile)
                {
                    for (int i = 0; i < Question.size(); i++)
                    {
                        OutFile << Question[i];
                    }
                    OutFile << endl;
                }
                Quantity--;
            }
        }
        else
            //没括号没小数
        {
            while (Quantity != 0)
            {
                string Question = Calc->MakeFormula();
                cout << Question << endl;
                if (Calc->IsFile)
                {
                    for (int i = 0; i < Question.size(); i++)
                    {
                        OutFile << Question[i];
                    }
                    OutFile << endl;
                }
                Quantity--;
            }
        }
    }
    delete Calc;
    system("pause");
}

4 运行结果

对于生成的算式,一共有四种情况,四种算式的结果如下:

4.1有括号没小数

软件工程(2019)结对编程第二次作业_第1张图片

输出到文件
软件工程(2019)结对编程第二次作业_第2张图片

4.2有括号有小数

输出到文件
软件工程(2019)结对编程第二次作业_第3张图片

4.3没括号有小数

软件工程(2019)结对编程第二次作业_第4张图片

输出到文件
软件工程(2019)结对编程第二次作业_第5张图片

4.4没括号没小数

软件工程(2019)结对编程第二次作业_第6张图片

输出到文件
软件工程(2019)结对编程第二次作业_第7张图片

5 总结

对于这次的结对编程任务,在知道题目之后,我和孙治朝同学没有想到该怎么解决问题,所以我们两个人交替充当驾驶员与领航员的角色,这样对于问题能有多角度的思考。我们选择的是四则运算生成器,相对于电梯调度会容易些,但是也会出现难点,比如生成的算式如何加括号才能保证加的位置不会出错,刚开始我们俩思考的是先生成完整的算式,然后将括号插进去。但是我们存储算式的数据结构是字符串,就需要保证插进字符串正确的位置,这可就需要判断太多的条件,所以最终这条路没走通。
后来经过别的同学的提示,我们改变了思路,在生成算式的同时就判断需不需要加括号,如果需要的话就加上,这样思考的话,需要判断的情况会少很多,所以我就尝试着写了段代码,看最终能否成功运行。运行的结果跟我想象的差别不大,只是对于临界条件的判断还需要修改。我们俩就决定使用我的方法进行开发,我作为驾驶员,孙治朝作为领航员,共同完成剩下的任务。
这次的结对编程任务,孙治朝同学非常积极,很早就开始着手编程,对于问题的思考也独特。我就比较懒了,拖延了几天之后才开始着手思考如何解决问题,所以说积极性方面,我需要向孙治朝同学学习。同时作为领航员,孙治朝同学对于代码的复审要求也很严格,这样严谨的风格对于团队合作有很大的帮助。

你可能感兴趣的:(软件工程(2019)结对编程第二次作业)