• 张宵 20200924-5 四则运算试题生成


    要求1 参考《构建之法》第4章两人合作,结对编程上述功能,要求每人发布随笔1篇 (代码是共同完成的,随笔有以下两种方式:(①允许两人写一份,得一份分数,二人均分;②如果每人写一份,内容不得抄袭,体会需要是自己的,分别得到自己博客的分数)。 (10分)

    (1) 给出每个功能的重点、难点、编程收获。

    • 功能1:四则运算

      重点及难点:随机出题

      编程收获:我们是把所有的功能点看完之后,整体编程的

    • 功能2:支持括号

      重点及难点:

      (1)括号的生成

       题意共有4个操作数,括号只能出现1个或两个,完全可以列举出所有出现的情况。但是为了迎接挑战,我们采取了自动生成括号的方式。

       1.分析可知:括号分为左右两种情况,右括号始终在操作数之后,左括号始终在操作数之前,4个操作数最多可以有3对括号

       2.当生成一个操作数后,紧接着的可能是操作符或者右括号

       3.可以分两组生成,一组操作数或者“(”;一组操作符或者”)“

       4.当左括号小于2生成操作数或”(“,否则只生成操作数;当右括号少于左括号生成操作符和右括号,否则生成操作符

      

      1 /*
      2 * 生成方程
      3 * pequa:存储方程的结构体
      4 */
      5 void rand_equa(pEqua pequa)
      6 {
      7     int left = 0;                                    //记录左括号
      8     int right = 0;                                    //记录右括号
      9     int index = 0;                                    //记录方程当前存储的位置
     10     bool bol = true;                                //true生成数字,false生成操作符
     11     //srand((unsigned)time(NULL));
     12     for (int i = 0; i < EQUATION_LENGTH;)            //每生成一次数字,生成一次操作符
     13     {
     14         if (bol)
     15         {
     16             //生成数字或“(”
     17             if (left < 2)                            //左括号小于2生成数字或括号,否则只生成数字
     18             {
     19                 LARGE_INTEGER seed;                    //设置随机种子,毫秒级
     20                 QueryPerformanceFrequency(&seed);
     21                 QueryPerformanceCounter(&seed);
     22                 srand(seed.QuadPart);
     23 
     24                 int rad = rand() % 4;
     25                 if (rad == 2 && bol)
     26                 {
     27                     strcpy_s(pequa->equa[index++], DATA_SIZE, "(");
     28                     left++;
     29                     bol = true;
     30                     continue;
     31                 }
     32                 else
     33                 {
     34                     strcpy_s(pequa->equa[index++], DATA_SIZE, rand_numb());
     35                     i++;
     36                     bol = false;
     37                     continue;
     38                 }
     39             }
     40             else
     41             {
     42                 //pequa->equa[index++] = rand_numb(false);
     43                 //char* pChr = rand_numb(false);
     44                 strcpy_s(pequa->equa[index++], DATA_SIZE, rand_numb());
     45                 i++;
     46                 bol = false;
     47                 continue;
     48             }
     49         }
     50         else
     51         {
     52             //生成操作符
     53             if (left > right)                        //右括号少于左括号生成操作符和右括号,否则生成操作符
     54             {
     55                 LARGE_INTEGER seed;
     56                 QueryPerformanceFrequency(&seed);
     57                 QueryPerformanceCounter(&seed);
     58                 srand(seed.QuadPart);
     59                 if ((rand() % 3) == 2 && bol)
     60                 {
     61                     strcpy_s(pequa->equa[index++], DATA_SIZE, ")");
     62                     right++;
     63                     bol = false;
     64                 }
     65                 else
     66                 {
     67                     LARGE_INTEGER seed;
     68                     QueryPerformanceFrequency(&seed);
     69                     QueryPerformanceCounter(&seed);
     70                     srand(seed.QuadPart);
     71                     int number = rand() % 4;
     72                     if (number == 0)
     73                     {
     74                         strcpy_s(pequa->equa[index++], DATA_SIZE, "+");
     75                     }
     76                     else if (number == 1)
     77                     {
     78                         strcpy_s(pequa->equa[index++], DATA_SIZE, "-");
     79                     }
     80                     else if (number == 2)
     81                     {
     82                         strcpy_s(pequa->equa[index++], DATA_SIZE, "*");
     83                     }
     84                     else
     85                     {
     86                         strcpy_s(pequa->equa[index++], DATA_SIZE, "/");
     87                     }
     88                     bol = true;
     89                 }
     90             }
     91             else
     92             {
     93                 LARGE_INTEGER seed;
     94                 QueryPerformanceFrequency(&seed);
     95                 QueryPerformanceCounter(&seed);
     96                 srand(seed.QuadPart);
     97                 int number = rand() % 4;
     98                 if (number == 0)
     99                 {
    100                     strcpy_s(pequa->equa[index++], DATA_SIZE, "+");
    101                 }
    102                 else if (number == 1)
    103                 {
    104                     strcpy_s(pequa->equa[index++], DATA_SIZE, "-");
    105                 }
    106                 else if (number == 2)
    107                 {
    108                     strcpy_s(pequa->equa[index++], DATA_SIZE, "*");
    109                 }
    110                 else
    111                 {
    112                     strcpy_s(pequa->equa[index++], DATA_SIZE, "/");
    113                 }
    114                 bol = true;
    115             }
    116         }
    117     }
    118     while (right < left)
    119     {
    120         //char pChr[] = ")";
    121         //pequa->equa[index++] = pChr;
    122         strcpy_s(pequa->equa[index++], DATA_SIZE, ")");
    123         right++;
    124     }
    125 }

      (2)运算优先级

      设定操作数、”+“或者”-“、”*“或”/“、”(“、”)“的优先级分别为0,1,2,3,4

      1.标记待入栈的优先级

      2.如果是数字和左括号入栈;如果是右括号,先计算括号内的值,再出栈左括号;如果为 + - ,计算后三个值再入栈,符号入栈;如果为 * / ,并且前一位运算符存在且前一位运算符为 * /,就进行运算。

     1 /*
     2 * 计算方程结果
     3 * equa:方程式
     4 * 返回char*
     5 */
     6 char* calculate_equa(pEqua equa)
     7 {
     8     calculate calc;                                        //定义计算式子的栈
     9     for (int i = 0; i < DATA_SIZE; i++)
    10     {
    11         //char* chr = equa->equa[i];                    //取一个符号
    12         if (strlen(equa->equa[i]) > 10 || strlen(equa->equa[i]) < 1 || equa->equa[i] == "" || equa->equa[i] == "")                    //判定数组数据无效
    13         {
    14             if (calc.rear == 0)
    15             {
    16                 return calc.equa[0];
    17             }
    18             //return calculate_three_result(&calc);        //取到末尾结束
    19             if (calc.rear == 2)
    20             {
    21                 return calculate_three_result(&calc);
    22             }
    23             if (calc.rear == 4)
    24             {
    25                 char pChr[DATA_SIZE];
    26                 strcpy_s(pChr, DATA_SIZE, calculate_three_result(&calc));
    27                 calculate_in(&calc, pChr);
    28                 return calculate_three_result(&calc);
    29             }
    30             /*while (calc.rear)
    31             {
    32 
    33             }*/
    34             return NULL;
    35         }
    36         char chr[DATA_SIZE];
    37         strcpy_s(chr, DATA_SIZE, equa->equa[i]);
    38         int flag = judge_flag(chr);        //记录待入栈符号的标记
    39         if (flag == 0 || flag == 3)
    40         {
    41             calculate_in(&calc, chr);    //如果是数字或左括号入栈
    42             continue;
    43         }
    44         if (flag == 4)
    45         {
    46             //char* ch = calculate_three_result(&calc);        //如果是右括号,计算括号内的值,再出栈左括号
    47             char pCh[DATA_SIZE];
    48             if (calc.flag[calc.rear - 1] == 3)
    49             {
    50                 strcpy_s(pCh, DATA_SIZE, calculate_out(&calc));
    51                 calculate_out(&calc);
    52                 calculate_in(&calc, pCh);
    53                 continue;
    54             }
    55             strcpy_s(pCh, DATA_SIZE, calculate_three_result(&calc));
    56             if (calc.flag[calc.rear] == 3)
    57             {
    58                 calculate_out(&calc);
    59                 calculate_in(&calc, pCh);                                        //计算结果入栈
    60                 continue;
    61             }
    62             calculate_in(&calc, pCh);                                        //计算结果入栈
    63             //char* pCh = calculate_three_result(&calc);
    64             strcpy_s(pCh, DATA_SIZE, calculate_three_result(&calc));
    65             calculate_out(&calc);
    66             calculate_in(&calc, pCh);                                        //计算结果入栈
    67             continue;
    68         }
    69         if (flag == 1 && calc.rear >= 2 && calc.flag[calc.rear - 1] >= 1 && calc.flag[calc.rear - 1] <= 2)    //如果为 + - ,计算后三个值,再入栈,符号入栈
    70         {
    71             //char* chr = calculate_three_result(&calc);
    72             char pCh[DATA_SIZE];
    73             strcpy_s(pCh, DATA_SIZE, calculate_three_result(&calc));
    74             calculate_in(&calc, pCh);
    75             calculate_in(&calc, chr);
    76             continue;
    77         }
    78         if (flag == 2 && calc.rear > 2 && calc.flag[calc.rear - 1] == 2)    //如果为 * / ,并且前一位运算符存在,前一位运算符为 * /,就进行运算
    79         {
    80             //char* chr = calculate_three_result(&calc);
    81             char pCh[DATA_SIZE];
    82             strcpy_s(pCh, DATA_SIZE, calculate_three_result(&calc));
    83             calculate_in(&calc, pCh);            //计算结果入栈
    84             calculate_in(&calc, chr);    //操作符入栈
    85             continue;
    86         }
    87         calculate_in(&calc, chr);
    88     }
    89     return NULL;
    90 }

      编程收获:

      上面这一方法似乎很麻烦,在这之前提出了经典的逆波兰方式,但是韩亚光同学的这一想法让我很吃惊,之前没有学习过,所以秉持着向他学习的理念,最终采用了这一方式。虽然逆波兰方式最终没有采用,但是加深了我对这一块的知识理解及栈的应用。在作业过程中我体会到基础知识是格外重要的,以往对数据结构的学习都是停留在理解上,看到的也是伪代码,很少真正的运用,所以在今后的学习中要加强这一方面。

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

      重点:命令行参数

      编程收获:由于之前的作业都涉及到了命令行参数,在这里只属于重点但不属于难点了。熟能生巧,多操练学习很多过往的难点都会被解决。

    if (argc == 3 && strcmp(argv[1], "-c") == 0)
        {
            for (int i = 0; i < strlen(argv[2]); i++) {
                if (argv[2][i] > '9' || argv[2][i] < '0')
                {
                    printf("题目数量必须是 正整数。
    ");
                    return 0;
                }
            }
            int num = atoi(argv[2]);                //题目数量
            pEqua pequ = create_equa_array(num);
    • 功能4. 支持分数出题和运算

      重点及难点:

      (1)带分数的出题

      (1)带分数题目的计算

        分数生成这里定义了一个分数类,随机生成两个数,构造成一个分数,计算运用到了符号重载,最小公倍数,最大公倍数等方法。

     1 fraction fraction::reduction()
     2 {
     3     int nume = abs(this->numerator);    //获取分子
     4     int deno = abs(this->denominator);    //获取分母
     5     if (nume == 0)
     6     {
     7         this->denominator = 0;
     8         return *this;
     9     }
    10     int g = gcd(nume, deno);        //获得分子和分母的最大公约数
    11     nume = nume / g;
    12     deno = deno / g;
    13     //判断数据正负号
    14     if ((this->numerator < 0 && this->denominator < 0) || (this->numerator > 0 && this->denominator > 0))
    15     {
    16         this->numerator = nume;
    17         this->denominator = deno;
    18         return *this;
    19     }
    20     else
    21     {
    22         this->numerator = -1 * nume;
    23         this->denominator = deno;
    24         return *this;
    25     }
    26 }

    (2)给出结对编程的体会

      因为本人编程能力不足,许多语法都不清楚,特别感谢本次结对编程搭档--韩亚光同学的耐心解答。

      首先,结对编程重在沟通。两个人编程和一个人编程的最大区别是需要与队友沟通,明确团队任务的内容及进度。在这一点上,我和韩亚光同学都是比较积极的,希望能尽早完成。在最初,韩亚光同学提出这个题他的思路的时候,我本人听的一知半解,但是他通过腾讯会议给我梳理了一遍他的思路及想法,使我确切的知晓了他编程的思路。在一些重难点上面我们也进行了有效的讨论及沟通,这一点我们团队做的较好。

      其次,结对编程促进学习。每个人写代码都会有自己特定的风格,我们在编程之初就约定了编程规范,并在编程过程中互相提醒,这就在互相发现问题并及时得到解决。在四则运算这个编程中,我们两人有两种不同的实现方式,互相学习了对方的思想,为之后学习编程提供了新的方法。

      最后,结对编程提高效率。当发现自己是团队中的一员时,总会要求自己不能拖后腿,按照约定时间完成项目进度,尽力发挥团队一员的作用。

    (3) 至少5项在编码、争论、复审等活动中花费时间较长,给你较大收获的事件。

      1.带括号算术式生成规则

        在这里,我们有不同想法,我认为可以穷举出括号的情况,直接填充生成;韩亚光认为如果之后有多余4个操作数的情况呢,提出了更优化的方案。这里我学习到了他的思想,不要急于求成,仔细观察表达式,在总结通用方法。

      2.算数式计算

        经典算法是将中缀表达式转换为后缀表达式,在对后缀表达式求解。我们在这里采用了一种不那么机智的办法,但是很新颖,总体思想和经典算法契合,采用标记法来判断当前栈的后三位是否需要计算。这里我们列举处理许多情况来验证是否可行,也花费了相当大的精力来完善。

      3.单元测试

        对单元测试框架、测试用例的编写不熟悉,花费了一定时间来学习。按照功能点设计的测试用例,韩亚光同学认为测试用例要多一点,我认为测试用例只需要包含到测试点就好,比如测试+、-、*、/这四个运算符,可以只有写两个测试用例即可,分数需要测到正分数、负分数、假分数,在于测试用例测的点,不用测很多组。

      4.分数计算

        韩亚光同学运用了符号重载,将所有的操作数变为分数再计算,这一点我认为他用的很巧妙,非常值得我学习。在分数这块,韩同学耗费了相当大的精力,因为最初的方案就是他想出来的,在实现的过程中遇到了许多问题,比如生成的方程式全是分数形式,这一点我们尚未优化。

      5.数组越界

        对字符数组的运用不熟练,导致相当一部分的错误是由于数组越界引起的。同时对字符指针有了更深的理解。指针这块是我的薄弱点,后续需要继续深入了解及运用。

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

    编程地点:计算机东楼220

    团队成员:张宵、韩亚光

      

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

    作业地址:https://e.coding.net/nenuwork/ourteam/arithmetic_operation.git

    提交记录:

      

  • 相关阅读:
    Transformation
    Yet Another Number Sequence
    Table
    K个联通块
    Acyclic Organic Compounds
    Sandy and Nuts
    tetrahedron
    BZOJ4750 密码安全
    实现商品秒杀 时间倒计时
    实现qq登录
  • 原文地址:https://www.cnblogs.com/ZigHello/p/13774107.html
Copyright © 2020-2023  润新知