个人项目:中小学数学卷子自动生成程序——队友代码点评

一、项目功能要求

  1. 命令行输入用户名和密码,两者之间用空格隔开(程序预设小学、初中和高中各三个账号,具体见附表),如果用户名和密码都正确,将根据账户类型显示“当前选择为XX出题”,XX为小学、初中和高中三个选项中的一个。否则提示“请输入正确的用户名、密码”,重新输入用户名、密码。
  2. 登录后,系统提示“准备生成XX数学题目,请输入生成题目数量(输入-1将退出当前用户,重新登录):”,XX为小学、初中和高中三个选项中的一个,用户输入所需出的卷子的题目数量,系统默认将根据账号类型进行出题。每道题目的操作数在1-5个之间,操作数取值范围为1-100。
  3. 题目数量的有效输入范围是“10-30”(含10,30,或-1退出登录),程序根据输入的题目数量生成符合小学、初中和高中难度的题目的卷子。同一个老师的卷子中的题目不能与以前的已生成的卷子中的题目重复。
  4. 在登录状态下,如果用户需要切换类型选项,命令行输入“切换为XX”,XX为小学、初中和高中三个选项中的一个,输入项不符合要求时,程序控制台提示“请输入小学、初中和高中三个选项中的一个”;输入正确后,显示“”系统提示“准备生成XX数学题目,请输入生成题目数量”,用户输入所需出的卷子的题目数量,系统新设置的类型进行出题。
  5. 生成的题目将以“年-月-日-时-分-秒.txt”的形式保存,每个账号一个文件夹。每道题目有题号,每题之间空一行。
  6. 个人项目9月28日早上8点以前提交至各自负责的助教处。提交方式:工程文件打包,压缩包名为“几班+姓名.rar”。

二、代码整体分析

  队友的个人项目使用C++语言完成,从总体上来看,代码使用了非常多的类,分别是RandommizeTestPaper,SystemPrompt,TreeNode以及User,分别用于生成题目,系统的提示以及用户的信息。其中二叉树用于生成题目,我认为这是非常指得敬佩的,因为其有效利用了数据结构课的二叉树知识,将操作数作为运算符的子节点,采用中序遍历,很简结的解决了出题问题,非常的棒。代码逻辑清晰,功能完备。

三、优点

  1.将所有的输入都作为字符串处理,采取“不信任输入”的方式处理用户的输入,对于要求输入题目的整数,对于!:".等符号也做了处理,同时对于数字的判断以及转换也十分巧妙

int SystemPrompt::IndentifyCommandType()
{
    // 退出登录
    if(m_command == "-1")
    {
        return -1;
    }

    // 切换用户
    if(m_command == "切换为小学")
    {
        return 1;
    }
    else if(m_command == "切换为初中")
    {
        return 2;
    }
    else if(m_command == "切换为高中")
    {
        return 3;
    }
    else if(m_command.find("切换") != string::npos)
    {
        return 4;
    }


    int length = m_command.length();
    if(length != 2)
    {
        return -2;
    }

    // 有前导0,就是不合法
    // 若就是0,也是不合法
    if(m_command[0] == '0')
    {
        return -2;
    }

    int number = 0;
    for(int i = 0; i < length; i++)
    {
        if(m_command[i]<'0'||m_command[i]>'9')
        number *= 10;
        number += m_command[i] - '0';
    }

    // 题目数不在范围
    if(number < 10 || number > 30)
    {
        return -2;
    }
    // 题目数在范围内
    return number;
}

2.巧妙的运用了二叉树这一逻辑结构处理题目的生成,对于所学知识的运用十分娴熟,同时也能很好的顾虑到括号的使用,不会出现诸如(1+1)+2这样的无用括号

string RandomizeTestPaper::GetLinearExpression(TreeNode* root)
{
    if(root == NULL)
    {
        return "";
    }
    if(root->IsLeaf())
    {
        return root->GetValue();
    }
    if(!root->GetLeft()->IsLeaf())
    {
        if(root->GetValue() == "*" || root->GetValue() == "/")
        {
            if(root->GetLeft()->GetValue() == "+" || root->GetLeft()->GetValue() == "-")
            {
                return "(" + GetLinearExpression(root->GetLeft()) + ")" + \
                       " " + root->GetValue() + " " + \
                       GetLinearExpression(root->GetRight());
            }
        }
    }
    if(!root->GetRight()->IsLeaf())
    {
        if(root->GetValue() == "*" || root->GetValue() == "/" || root->GetValue() == "-")
        {
            if(root->GetRight()->GetValue() == "+" || root->GetRight()->GetValue() == "-")
            {
                return GetLinearExpression(root->GetLeft()) + \
                       " " + root->GetValue() + " " + \
                       "(" + GetLinearExpression(root->GetRight()) + ")";
            }
        }
    }
    return GetLinearExpression(root->GetLeft()) + \
           " " + root->GetValue() + " " + \
           GetLinearExpression(root->GetRight());
}

四.缺点:

1.虽然使用类可以提高代码的封装性,但是对于这次编程的一个小项目,过多的类会造成阅读不便,如果没有一个好的IDE,要想理清代码的逻辑顺序需要多次切换cpp来查看,经常会切换错。同时虽然代码写的简洁易懂,但还是缺少必要的注释,只有每个函数开始时有一些阐明作用的注释,一些关键处较为晦涩的代码并未添加注释,不利于理解代码。

2.程序中没有加入使程序停止的代码,每次出题完毕后会进入下一轮出题,要想关闭只能用ctrl+c强制停止,如果从应用角度出发,我认为这一点是有缺陷的。

总结:

代码构思精妙,数据结构运用娴熟,同时功能完善,目前的测试没有出现任何bug,尽管代码注释较少,但良好的编程规范以及清晰的逻辑使得这一缺点不影响大局,总体来说是一个非常好的完成情况,值得我多次学习。

posted @ 2020-09-30 21:13  小明的叔叔  阅读(89)  评论(0)    收藏  举报