个人项目评测----自动生成中小学数学题

在阅读并测试了该同学的代码后,我认为该同学的个人项目实现了要求的所有功能。

 

他的代码有以下几点值得我学习的地方:

1、 程序的模块分工很明确,分别设置了AccountManagerFileOperator、PaperGenerator三个类用于进行账户控制、文件读写控制和试卷的生成,每个类的外部接口以及内部辅助函数的分工也很清晰;这样很有助于之后对程序进行维护和功能扩展。此外采用了枚举类型来表示不同的操作数和操作符,规划很合理。

 

 

2、 细节方面比较注重,考虑到了文件路径不存在时自动逐级创建的应对方案,也考虑到了在控制台直接向int型数据输入包含非数字的字符串会导致程序崩溃的情况。

 

文件目录创建代码:

bool FileOperator::folderConfirm(string address)

{

    //检查文件夹属性是否合法,不合法则说明文件夹不存在

    if (GetFileAttributes(address.c_str()) == INVALID_FILE_ATTRIBUTES)

    {

        vector<string> dirs; //逐级保存文件夹路径

        int len = address.length();

        string temp = "";

 

        //由于CreateDirecotry只能自动创建一层目录,故路径不存在时需要逐级创建目录完成总路径的创建

        //逐级拆分目录

        for (int i = 0; i < len; i++)

        {

            if (address[i] == '\\' || address[i] == '/') //要进入下一级文件夹则保存本级文件夹的名称

            {

                dirs.push_back(temp);

                temp = "";

            }

            else //否则继续记录本级文件夹名称

            {

                temp += address[i];

            }

        }

        dirs.push_back(temp);

 

        //逐级调用CreateDirectory函数进行目录创建

        len = dirs.size();

        temp = dirs[0];

        for (int i = 1; i < len; i++)

        {

            temp += "\\" + dirs[i];

            CreateDirectory(temp.c_str(), NULL); //尝试创建当前级文件夹目录(目录存在的话不会重新创建并覆盖原文件)

        }

        

        return false; //表示输入的路径先前并不存在

    }

    return true; //表示输入的路径本来就存在

}

该代码先检查了文件路径是否存在,在检查结果为不存在时,将文件路径逐级拆分到了vector容器中,之后使用了Windows API CreateDirectory函数来逐级创建文件夹路径,该函数在路径不存在时会创建文件夹并返回1,如果路径存在则不会创建文件夹并返回0。

 

 

3、历史题目的处理方面比较合理,在文件路径确定后一次性读取当前账户的所有历史题目并存在向量容器中,避免了每次查重都要读取一次文件。

他的文件读取函数:

vector<string> FileOperator::Read(string userName)

{

    vector<string> historyItems; //保存历史试题的容器

    string address = folderAddress + "\\" + userName; //根据用户名生成用户文件路径

    

    ifstream curfile;

 

    //读取指定路径下的所有.txt文件的内容

    _finddata_t file; //存储当前文件信息的结构体

    long lf = 0;

    if ((lf = _findfirst((address + "\\*.txt").c_str(), &file)) != -1) //设置仅搜索所有后缀为.txt的文件

    {

        do 

        {

            //处理当前搜索到的文件

            string dir = address + "\\" + file.name; //生成当前文件的绝对路径

            curfile.open(dir); //打开当前文件

            if (curfile.is_open())

            {

                string line;

                while (!curfile.eof()) //逐行读出题目并保存在历史试卷容器中

                {

                    getline(curfile, line);

                    if (line.length() > 4) //保存试题

                    {

                        historyItems.push_back(line.substr(4)); //存入子字符串是为了舍去行号标注

                    }

                }

                curfile.close(); //关闭当前文件

            }

        } while (_findnext(lf, &file) == 0); //当存在下一个.txt文件时继续处理

    }

    _findclose(lf); //关闭打开的搜索

    return historyItems;

}

 

同时我觉得他的代码有以下几点可以改进的地方:

1、公式中添加括号的函数可以改进,他的括号添加函数存在为一个操作数添加括号的情况也存在括起了整个公式的情况,有一定的随机性,有改进的空间。

他的括号生成函数:

void PaperGenerator::bracketAdder(vector<string>&operands, int num)

{

    int length = operands.size();

    int pos = 0; //括号起点

    int span = 0; //括号跨度

    for (int i = 0; i < num; i++) //添加num对括号

    {

        pos = rand() % (length - 1);

        span = rand() % (length - pos - 1) + 1;

 

        operands[pos] = "(" + operands[pos]; //起始操作数左侧设置左括号

        operands[pos + span] = operands[pos + span] + ")"; //终止操作数右侧设置右括号

    }

}

从代码中可以看出其括号的位置虽然是随机生成的,但在其括号位置生成后没有对被添加括号的操作数的两侧进行判断,造成了可能出现的括号仅括住了一个操作符或括住了整个公式的问题。

 

2、题目的查重有优化的地方,他采用的是逐一比较的方式,由于每生成一道题就要进行一次查重,当题量过多时该方法会成为性能瓶颈,可以对历史试题列表进行排序之后在查重时进行二分查找。

 

 

 

 

程序测试:

登录:

 

 

主界面:

 

 

生成试卷:

 

 

输出的试卷:

 

 

posted @ 2018-09-26 19:11  刘明杰  阅读(190)  评论(0)    收藏  举报