zoukankan      html  css  js  c++  java
  • 20180925-6 四则运算试题生成

    作业要求参照[https://edu.cnblogs.com/campus/nenu/2018fall/homework/2148]

    要求1 参考《构建之法》第4章两人合作,结对编程上述功能,要求每人发布随笔1篇 (代码是共同完成的,博客是分别完成的)。 (1) 给出每个功能的重点、难点、编程收获。(2)给出结对编程的体会,以及 (3) 至少5项在编码、争论、复审等活动中花费时间较长,给你较大收获的事件。 (10分)

    功能一:重点是随机数和随机符号的实现。在进行此功能的实现时,遇到了两种错误的情况,一种是每次生成的四个随机数和三个操作符号都是一样的,比如“4+4+4+4=”这种,还有一种就是第一次运行生成的20个算式没问题,但是第二次运行会生成和第一次一模一样的算式。后来我们用以时间作为种子,完成随机数和操作符的实现,学会了如何正确产生随机数。

    int num[8]={0};
    char sign[4]={};
    for(int i=0;i<4;i++)
    {
        sign[i]=RandomSign();
    }
    for(int i=0;i<8;i++)
    {
    num[i]=rand()%9+1;
    }

    计算的时候我们通过生成的中缀表达式转换为后缀表达式,之后再利用后缀表达式来计算。下面给出关键代码:

    void RPNotation(vector<char>&st,vector<char>ve)
    {
        st.clear();
        stack<char>ssign;
        for(int i=0;i<ve.size()-1;i++)
        {
            if(ve[i]>='0'&&ve[i]<='9')
                st.push_back(ve[i]);
            else
            {
                if(ssign.empty()||ve[i]=='(')
                    ssign.push(ve[i]);
                else
                {
                    if(ve[i]==')')
                    {
                        while (ssign.top()!='(')
                        {
                            st.push_back(ssign.top());
                            ssign.pop();
                        }
                        ssign.pop();
                    }
                    else
                    {
                        if(ve[i]=='*'||ve[i]=='/')
                        {
                            while(!ssign.empty()&&(ssign.top()=='*'||ssign.top()=='/')&&ssign.top()!='(') {
                                st.push_back(ssign.top());
                                ssign.pop();
                            }
                            ssign.push(ve[i]);
                        }
                        else
                        {
                            while(!ssign.empty()&&ssign.top()!='(')
                            {
                                st.push_back(ssign.top());
                                ssign.pop();
                            }
                            ssign.push(ve[i]);
                        }
                    }
                }
            }
        }
        while(!ssign.empty())
        {
            st.push_back(ssign.top());
            ssign.pop();
        }
    }
    View Code

    功能二:重点在于如何生成括号,以及如何匹配左右括号。一开始孙赛佳的想法是以一定概率产生左括号,同时用一个数记录左括号个数,当这个数不为零时再以一定概率产生右括号,等式结束时,检查并生成对应左括号数的右括号。但是实现起来就比较困难。然后我们想了想说就四个操作数,能调整的优先级情况就几种,直接用二元数组就行。后来采用的就是这个办法,节省了不少时间。

    int bracket[9][8]=
    {
        { 0, 0, 0, 0, 0, 0, 0, 0 },
        { -1, 0, 0, 1, 0, 0, 0, 0 },
        { 0, 0, -1, 0, 0, 1, 0, 0 },
        { 0, 0, 0, 0, -1, 0, 0, 1 },
        { -1, 0, -1, 0, 0, 2, 0, 0 },
        { -2, 0, 0, 1, 0, 1, 0, 0 },
        { -1, 0, 0, 1, -1, 0, 0, 1 },
        { 0, 0, -2, 0, 0, 1, 0, 1 },
        { 0, 0, -1, 0, -1, 0, 0, 2 }
    };

    功能1,功能2实现的截图。

    功能三:重点是实现将生成的算式输出到文本中去同时避免重复。这儿使用FILE指针建立打开文本实现输出的。每次生成一个算式就放入map中去,后续生成算式先去map中查询是否出现过,如果出现过,就继续生成,直到没有出现过。

    下面给出写入文件的代码。

    FILE *fp=fopen("题目.txt","w");
    for(int i=0;i<totalnum;i++)
    {
        CreateEquation(vec,correctAns);
        int j;
        for(j=0;j<vec.size();j++)
        {
            printf("%c",vec[j]);
            fprintf(fp,"%c",vec[j]);
        }
        for(;j<50;j++)
        {
            printf(" ");
            fprintf(fp,"%c",32);
        }
        Fraction f1=(correctAns);
        f1.Print();
    }

    功能3实现的截图

    功能四:重点是要计算分数,结果约分化简。这里我们实在想不出,就去网上查了不少资料,最后发现一种通过连分数的方法将小数转化为分数。

    同时我们求出最大公约数,来对算式进行化简,而结果能分子分母能整除的直接输出相除之后的结果,不能整除的能化为带分数的化为带分数,不能的直接输出约分后的结果。

    而分数我们在生成时也添加了一对括号,这样方便浏览,因为,如果出现1/3+1/2/3/4/5/6这样的不如(1/3)+(1/2)/(3/4)/(5/6)这样看的直观。

    下面给出部分重要代码:

    int gcd(int m, int n) {
        int a=abs(m);
        int b=abs(n);
        return (a % b == 0) ? b : gcd(b, a % b);
    }
    void Fraction::Print()
    {
        //printf("%d,%d    ",int(a),int(b));
        if(int(a)%int(b)==0)
        {
            std::cout << (sign ? "-" : "") << int(a)/int(b) << std::endl;
        }
        else if(int(a)>int(b))
        {
            int fz = int(a);
            int fm = int(b);
            int zh = fz / fm ;
            int sh = fz - zh * fm ;
            std::cout << (sign ? "-" : "") << zh <<" "<<sh<< "/" << fm  << std::endl;
        }
        else
        {
            std::cout << (sign ? "-" : "") << a << "/" << b << std::endl;
        }
    }
    for (size_t i = firstIndex; i < nums.size(); i++)
    {
        confrac.nums.push_back(nums[i]);
        Fraction fr(confrac);
        double d = fr.ToDouble();
        vfr.push_back(fr);
        vesp.push_back(abs(d - a));
    }

    功能4实现的截图

    体会:通过这次结对编程,我体会到了不一样的编程体验。一开始我提议使用python,因为我对python比较熟悉。孙赛佳提议使用C++,产生了分歧,后来经过讨论决定使用C++。缩进方面我一直都是Tab党,孙赛佳制定了代码规范,要求4个空格缩进,这次也是狠狠的教训了我一下,在我编写时时刻都要注意自己有没有遵守规范。在对于除零问题上,我开始只认为1~9的运算就能避免出现除零的问题,但实际检验发现9/(1-1)这样也会发生除零的问题,后来决定使用一个判断函数来检测/号后面的数是否为零,为零就重新执行,直到生成的算式没有除零错误。其实编码时人和人的习惯就是不一样,很多地方都不同,这次也谢谢我的结对伙伴同我商讨制定了规范,让我这种平时代码不遵守格式的人学到了很多。

    在编码、争论、复审等活动中花费时间较长,给你较大收获的事件:

    1.这次的编码是按照规范来编写的,并没有按自己以往的风格,虽然收到了一定的制约,但确有收获。

    2.同结对伙伴一同学习了逆波兰式,虽然过去有学过,但已经忘了,学习时先在纸上模拟计算,演练算法,而后,由我负责指挥,结对伙伴负责编写这部分代码。

    3.结对编程可以说是这次非常新的一个体验,我做指挥时,总会有别的想法,比如,这里我们不安刚才的来,换一下行不行。伙伴就会说那你来(笑)。而后证明了当时的突发奇想大多是没有正确的结果的。我在编码时,由于平时编码不注重规范,伙伴会提醒我,这里要感谢他。

    4.学习了单元测试,这方面我们都没有经验,中途曾想要更换语言(C#),因为测试的资料比较多,后来同伴坚持用原来的语言,一同学习了TDD,查阅了资料,收获颇多。

    5.在除零的问题上确实争论了很久,我想用1-9来避免,后来测试发现,少量的试题不会产生,当试题数量多的时候就有了。我有想那除号后面没有减号不就得了,同伴说,那除号后面判断不是零不是更容易吗,之后就用了判断函数来实现。

    要求2 给出照片1张,包括结对的2位同学、工作地点、计算机,可选项包括其他能表达结对编程工作经历的物品或场景。 (5分)

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

    git代码地址[https://coding.net/u/shishishaonian/p/four_arithmetic_operation]

  • 相关阅读:
    PHP导出sql文件
    BugScan插件编写高(gǎo)级(jī)教程
    php父级目录文件包函问题
    检测web服务器指定位置大文件是否存在
    解决Linux关闭SSH,终端后运行程序终止问题(包括后台)
    Python Matplotlib绘图库 安装
    校园网突围之路由器开wifi__windows版
    [openwrt 项目开发笔记]: 传送门
    [Openwrt 项目开发笔记]:PHP+Nginx安装(七)
    [Openwrt 项目开发笔记]:MySQL配置(六)
  • 原文地址:https://www.cnblogs.com/orion1994/p/9756253.html
Copyright © 2011-2022 走看看