20200924-5 四则运算试题生成,结对

此作业要求参见:https://edu.cnblogs.com/campus/nenu/2020Fall/homework/11245

功能1. 四则运算

支持出题4个数的四则运算题目,所有题目要求作者有能力正确回答 (提示:1/3 != 0.33333333333333333333333333333333,而是无限长)。

重点/难点:

首先需要考虑算式的规则与随机性。在题目要求算式需要包含四个操作数和三个运算符,所以会加减与乘除会同时出现,需要考虑运算符的优先级。同时还考虑到用户是小学生,所以要避免被除数不能够被除尽的情况。

重要代码示例:

//中缀表达式转后缀表达式
void in_to_post(char* inorder,char* &post)
{
    stack <char> S;
    int k=0;
    while(*inorder)
    {
        //操作数
        if(*inorder>='0'&&*inorder<='9')
        {
            post[k++]=*inorder;
        }
        //运算符
        else
        {
            if(S.empty())
            {
                S.push(*inorder);
            }
            else if(*inorder=='(')
            {
                S.push(*inorder);
            }
            else if(*inorder==')')
            {
                while(S.top()!='(')
                {
                    post[k++]=S.top();
                    S.pop();
                }
                S.pop();
            }
            else
            {
                while(prio(*inorder)<=prio(S.top()))
                {
                    post[k++]=S.top();
                    S.pop();
                    if(S.empty()){
                        break;
                    }
                }
                S.push(*inorder);
            }
        }
        inorder++;
    }
    if(!S.empty())
    {
        char c=S.top();
        post[k++]=c;
        S.pop();
    }
}
//后缀表达式求值
double postcal(char* post)
{
    stack <double> s;
    double temp;
    while(*post)
    {
        if(*post>='0'&&*post<='9')
        {
            temp=*post -'0';
            s.push(temp);
        }
        else
        {
            double a=s.top();
            s.pop();
            double b=s.top();
            s.pop();
            double c=cal(b,a,*post);
            s.push(c);
        }
        post++;
    }
    return s.top();
}

运行效果图:

 

编程收获:

在实现功能一的过程中,使用了本科数据结构中所学习的栈的知识点。在编写四则运算法则时,我们将其封装成函数,方便调用。

功能2. 支持括号

重难点:

功能2的重难点在于要保证括号的随机性和正确性。由于是四则运算,所以括号出现次数不会大于2次。

重要代码示例:

  //在字符前后添加空格
    for(int i=pos-1; i>=0; i--)
    {
        dataStr[i*2+1]=dataStr[i];
        dataStr[i*2+2]=' ';
    }
    dataStr[0]=' ';
    //随机生成括号个数
    temp = random(2);
    //添加第一对括号
    if(dataStr[temp*4+3]=='-')
    {
        temp=0;
    }
    dataStr[4*temp]='(';
    dataStr[4*temp+6]=')';
    char c=' ';
    dataStr = remove_char(dataStr,strlen(dataStr),c);

    int temp1 = random(3);
    //判断是否添加第二对括号
    if(temp1==2&&temp<2)
    {
        dataStr[10]=dataStr[8];
        dataStr[9]=dataStr[7];
        dataStr[8]=')';
        for(int i=6; i>=0; i--)
        {
            dataStr[i+1]=dataStr[i];
        }
        dataStr[0]='(';
        dataStr[pos+4]='';
    }
    else
        dataStr[pos+2]='';

运行效果图:

 

编程收获:

由于要保证表达式出现的随机性,所以推断对号最多出现位置为2次,而出现位置为4个。所以我们设计先随机将两个操作数和一个运算符括起来,再将这个含括号的部分看成一个整体,随机添加括号。

功能3. 限定题目数量,"精美"打印输出,避免重复

重难点:

功能3的重难点在于判断输入的值是否有效,并且按照答案与题目横向对齐、输出在文件的右边的格式打印成txt。

重要代码示例:

    //命令行参数题目数量控制和差错检验
    if(argc == 3 && !strcmp(argv[1],"-c"))
    {
        if (strspn(argv[2], "0123456789") == strlen(argv[2]))
        {
            n = std::atoi(argv[2]);
            output_print(n);
        }
        else
        {
            cout<<"题目数量必须是 正整数。"<<endl;
        }
    }
    else
    {
        for(int i=0; i<n; i++)
        {
            create(4,inorder);
            cout<<inorder<<"="<<endl;
            cout<<"?";
            cin>>answer;
            in_to_post(inorder,post);
            double res = postcal(post);
            output(res,answer,score);
        }
        cout << "你一共答对" << score << "道题,共"<< n <<"道题。" << endl;
    }

运行效果图:

编程收获:

鉴于之前对命令行参数的经验,很快理解了这道题,但是校验题目个数的值上花费了一定时间。

功能4. 支持分数出题和运算

重难点:

功能4的重难点在分数的四则运算需要考虑的特殊情况比整数的四则运算要多。

重要代码示例:

//分数化简
struct num simplify(struct num a)
{
    for(int i=a.uper; i>=1; i--)
    {
        if((a.uper%i==0)&&(a.lower%i==0))
        {
            a.uper=a.uper/i;
            a.lower=a.lower/i;
        }
    }
    return a;
}

//后缀表达式求值
struct num cal(struct num a,struct num b,char c)
{
    if(c=='+')
    {
        return unifiedAdd(a,b);
    }
    else if(c=='-')
    {
        return unifiedDel(a,b);
    }
    else if(c=='*')
    {
        if(a.fraction==0&&b.fraction==0)
        {
            a.uper = a.uper*b.uper;
            a.lower = a.lower*b.lower;
            a = simplify(a);
            return a;
        }
        else if(a.fraction==1&&b.fraction==1)
        {
            a.integer = a.integer*b.integer;
            return a;
        }
        else
        {
            if(a.fraction==1)
            {
                b.uper = a.integer*b.uper;
                return b;
            }
            else
            {
                a.uper = a.uper*b.integer;
                return a;
            }
        }
    }
    else
        return a;
}

运行效果图:

编程收获:

由于一开始选择了较为熟悉的c++语言,导致编写分数的四则运算时花费了大量的时间。所以选择编程语言也是很重要的环节。

结对编程体会

结对编程让我体会到了团队的力量,一个复杂的项目由两个人完成能够顺利许多。在算法思路的讨论中,我也从中学习到了许多,也积累了一些经验。由于自己的逻辑能力较差,所以主要的编程任务均由龚万福同学完成,非常感谢他对于本次作业的贡献。

花费时间较长的事件:

(1)由于没有完全理解老师的问题,导致总会遗漏一些要求,在整理代码的时候要重新抽出时间完成遗漏的点。

(2)设计和编写在四则运算中随机生成括号的算法。

(3)在编写分数的四则运算时,遇到了许多预料之外的问题。

收获较大的事件:

(4)复习了前缀表达式转后缀表达式,以及后缀表达式的运算。

(5)在编程前一定要了解清楚需求再选择编程语言。

要求2 给出结对开发的截图证据,要求截图能够证明你们是在结对编程。 (5分)

由于疫情原因,我们通过teamviewer共享屏幕和线上交流完成这次的结对编程。

 

要求3 使用coding.net做版本控制。checkin 前要求清理 临时文件、可执行程序,通常执行 build-clean可以达到效果。(25分)

coding地址:https://e.coding.net/siahu/arithmetic/f_4.git

原文地址:https://www.cnblogs.com/siahu/p/13745291.html