中小学数学卷子自动生成程序-结对编程之队友代码分析

一、编程语言:C++

二、代码解读与分析:

1、 项目逻辑

通过输入信息与数组中存储账户信息比对完成用户登录。根据不同的类型要求生成相应类型的试卷并查重。将生成题目以指定的形式存放在指定文件夹下。过程中可切换试卷类型。

2、函数功能说明

int main():主函数负责调用各个功能函数,将各个功能模块按照项目实际的使用情况与应用逻辑结合适当的提示性信息进行组织和套用。

void SetPrimary(int n):生成小学类型的试卷。

void SetMiddle(int n):生成初中类型的试卷。

void SetHigh(int n):生成高中类型的试卷。

string GetTime():获取并以字符串形式返回当前计算机系统时间。

void CreateFile(int n,int m):在当前文件夹下,按照.\账户名\试卷类型的路径判断文件夹是否存在,若不存在则生成新的文件夹。

void StoreFile(string na,int n):将生成的题目以“年-月-日-时-分-秒.txt”的形式保存在账户文件夹下。

int GetFile(string na):获取当前账户以前已生成的所有卷子中的题目。

void CheckFile(string na,int sub,int pronum):对当前新生成的试卷内容与同一老师文件夹下之前生成的题目查重,若存在重复题目就重新生成新的题目替换。

int ChangeModel():切换用户生成试卷的类型或者返回退出系统。

3、函数代码分析与优缺点解读

(1)void SetPrimary(int n)

①代码分析:

spenum代表的是括号的情况,在小学题目里只在含有3/4/5个数字的时候才会产生括号。比如随机得到的数字式子3 23 12 4,然后随机得到的是spenum[1],那么最后的式子就是(3+23)-12-4。除了括号外的加减乘除的计算是在另一个数组里进行随机掉落。即整体是数字先与括号结合,然后数字括号作为整体再和运算符号结合。

但经考虑校验不难发现,括号的枚举情况是不全面的,而且由于int类型起始数字0会被自动省略,无法确保spenum[]内部数字的位数相同,这些原因都致使SetPrimary没有真正实现涵盖全部情况的随机生成。

以表达式中含3个操作数为例,利用了整数的取余取模运算实现到字符的逻辑映射关系。操作数为4和5个时的原理相同。

if(numb==3)
{
    int spenum[]={0,120,12};
        cc=rand()%3;
    for(int i=0;i<3;i++)
    {
        spe[i]=spenum[cc]%10;
        spenum[cc]=spenum[cc]/10;
    }
}

括号外的加减乘除的运算符随机生成与数字括号结合。

for(int i=0;i<numb;i++)
{
    bb=rand()%100+1;
    itoa(bb,c,10);
    if(spe[down]==1)
    {
        g_a[n]+=spesign[1];
    }
    g_a[n]+=c;
    if(spe[down]==2)
    {
        g_a[n]+=spesign[2];
    }
    if(numb-1==i)
    {
        break;
    }
    g_a[n]+=signal[(rand()%4)] ;
    down--;
}
g_a[n]+=‘=‘;

 ②优点:解题思路新颖。

虽然没有完整实现一个表达式插入括号的随机化,但是在队友自己设定的几种插入括号的可能情况中,队友巧妙的利用了整数的取余取模运算实现到字符的逻辑映射关系,这个解题思路很新颖。

 ③缺点:未完全实现括号插入的随机化。

队友默认一个数字的至多添加一对括号,忽略了诸如(((2+3)+4)+5)+6=,(2+3)+(4+5)=等的合理性情况。对括号的可能性采取枚举的方法,因而该功能代码的拓展性不强。

(2)void SetMiddle(int n)

 ①代码分析:

通过随机数的生成、int类型与string类型的转换、字符串的操作,结合数组,实现数字和运算符号的组合表达式。

char c[5];
int numb=rand()%4+1,pnum,csign,sum=0;
for(int i=0;i<numb;i++)
{
    pnum=rand()%100+1;
    csign=rand()%3;
    sum+=csign;
    itoa(pnum,c,10);
    if(csign==0)
    {
        g_a[n]+=spesign[0];
    }
    g_a[n]+=c;
    if(csign==1)
    {
        g_a[n]+=spesign[1];
    }
    if(numb-1==i)
    {
        break;
    }
    g_a[n]+=signal[(rand()%4)] ;
}

②优点:常见语句书写规范。

③缺点:功能考虑情况不全面,没有括号的随机插入。

    char signal[]={‘+‘,‘-‘,‘*‘,‘/‘};
    string spesign[]={"√","^2"};

(3)void SetHigh(int n)

方法与void SetMiddle(int n)基本一致,此处不再赘述。

功能考虑情况不全面,没有括号的随机插入。

(4)string GetTime()

  ①代码分析:

获得系统时间。

string GetTime()
{
    time_t t = time(0);
    char clock[30],ymd[30];
    strftime(ymd,sizeof(ymd),"%Y-%m-%d",localtime(&t) );
    string stime(ymd);
    stime+=‘-‘;
    strftime(clock,sizeof(clock),"%X ",localtime(&t) );
    string ss(clock);
    for(int i=0;i<ss.length();i++)
    {
        if(ss[i]==‘:‘)
        {
            stime+=‘-‘;
        }
        else stime+=ss[i];
    }
    return stime;
} 

  ②优点:灵活应用函数解决问题。

(5)void CreateFile(int n,int m)

void CreateFile(int n,int m)
{
    string folderPath = ".\\"+g_name[n];
    if (access(folderPath.c_str(), 0)!=0)
    {
        mkdir(folderPath.c_str());
    }
    if(m==1)
    {
        folderPath+="\\小学";
    }
    else if(m==2)
    {
        folderPath+="\\初中";
    }
    else
    {
        folderPath+="\\高中";
    }
    if (access(folderPath.c_str(), 0)!=0)
    {
        mkdir(folderPath.c_str());
    }
}

  优点:灵活应用函数解决问题。

(6)void StoreFile(string na,int n)

void StoreFile(string na,int n)
{
    string name=na+".txt";
    ofstream csout(name.c_str(),ios::out);
     for(int i=0;i<n;i++){
        csout<<i+1<<‘:‘<<g_b[i]<<endl;
        csout<<endl;
    }
    csout.close();
    cout<<"*****试卷已保存至 "<<name<<" 中*****"<<endl;
}

(7)int GetFile(string na)

①代码分析

利用文件流,将同账户下所有的文件中的题目都整合保存在一个string数组中,便于之后的使用。

②优点:灵活应用函数解决问题。

(8)void CheckFile(string na,int sub,int pronum)

  ①代码分析

整体我觉得这个函数逻辑可以分成三个阶段:一个是对当前账户已经存在的文件的题目预处理,进行一个计数排序。然后是有选择性的对长度相同的题目进行查重。最后是如果有重复就重新生成题目然后替换。

把该账户以前的题目按题目长短排序,然后新产生的题目直接和他字数相同的题目进行比较,如果没有字数相同的那就可以直接过掉,用ifstream读取输出的时候没有发现有空行。

首先应用了一个计数排序。第一出现时count是存对应长度的string的个数,count[2]=3意味着长度为3的字符串有3个。sum1存对应长度的string的个数。之后count存的是包括比他长度小以及等于的字符串个数。Str[]存储的是按长度排序后的字符串。Replace[]是记录重复题目编号的数组。while循环开始正式查重。pronum是产生的题目数量。查重时根据字数查重,只查过去题目中题目长度和当前题目长度相同的题目,flag记录重复题目数量。switch(sub)这里sub是题目的难度,然后进行题目替换,且只对重复的题目替换。g_b就是最后得到的题目。

这个子函数的代码还是有细细阅读的必要的。

void CheckFile(string na,int sub,int pronum)
{
    int count[30]={0},sum1[30]={0},judge[30]={0},flag=0,replace[30]={0},an=0;
    int k=GetFile(na);
    string str[k];
    for(int i=0;i<k;i++)
    {
        int number=g_s[i].length()-1;
        count[number]++;
    }
    sum1[0]=count[0];
    for(int i=1;i<40;i++)
    {
        sum1[i]=count[i];
        count[i]+=count[i-1];
    }
    memcpy(judge,count,sizeof(count));
    for(int i=k-1;i>=0;i--)
    {
        int number=g_s[i].length()-1;
        str[count[number]-1]=g_s[i];
        count[number]--;
    }
    for(int i=0;i<30;i++)
    {
        replace[i]=-1;
    }
    for(int i=0;i<pronum;i++)
    {
        g_b[i]=g_a[i];
    }
    while(1)
    {
        flag=0;
        for(int i=0;i<pronum;i++)
         {
             int number=g_b[i].length()-1,m;
             if(sum1[number]==0){continue;}
             else{
                 for(int j=judge[number-1];j<judge[number];j++)
                 {
                     if(g_b[i]==str[j]){
                         replace[flag]=i;
                         flag++;
                         break;
                     }
                 }
             }
         }
         if(flag==0)
         {
             break;
         }
         switch(sub)
         {
             case 1:
                 SetPrimary(flag);
                 break;
             case 2:
                 SetMiddle(flag);
                 break;
             case 3:
                 SetHigh(flag);
                 break;
             default:
                 break;
         }
         for(int i=0;i<30;i++)
         {
             if(replace[i]!=-1)
             {
                 g_b[replace[i]]=g_a[an];
                 an++;
                 replace[i]=-1;
             }
             else break;
         }
    }
    cout<<"***** 得到的卷子中的题目是: *****"<<endl;
     for(int i=0;i<pronum;i++)
     {
         cout<<i+1<<"、 "<<g_b[i]<<endl;
    }
     cout<<endl;
}

   ②优点

我个人觉得队友这个函数写的很棒。其实文件的查重我个人觉得在整体工程性能是个有很大优化空间的模块,可实现的方式有很多种,但不同种的写法根据对文件流的应用在性能上的差别还是很大的。队友相当于对所有的已有题目进行了一个预处理放在一个string数组中,看似可有可无的预处理却实际是在后面的操作进行操作对象的排除,这一点是在实际项目的制作过程中需要注意的。

(9)int ChangeModel()

  ①代码说明:

简单的字符串与int的逻辑映射。

(10)int main()

  ①代码说明:

中规中矩的根据程序使用逻辑实现了各个子模块的函数调用。

4、项目代码整体优点缺点点评总结

(1)优点

①函数命名规范

②常见语句书写书写规范

③灵活应用类似mkdir(),access(),strftime(),itoa(),memcpy()等函数解决实际问题。void CheckFile(string na,int sub,int pronum) 模块设计思路较为新颖,应用到了计数排序。多处利用了类似int和string不同类型数据之间逻辑映射以更方便的编写代码。

④代码中将各个功能模块尽可能细分然后编写子函数,这样代码的模块化很清晰,方便代码的正确定检验和逻辑的梳理。

(2)缺点

①功能实现不完整。小学类型出题没有考虑括号插入的全部情况。初中高中类型的试卷忽略了括号运算符。

②代码对初读者的可读性还有待提高,建议在关键思路的地方给予简明的注释。

③部分变量命名不规范

5、优化意见

(1)完善void SetPrimary(int n),void SetMiddle(int n),void SetHigh(int n)功能。

(2)在代码中添加适当的注释增加代码的可读性。

6、个人感悟

同样的一个项目,同样的一份项目需求,但是不同的人用自己不同的思路就可以有很多细节处理不用的方式。通过仔细阅读队友的代码,认真的分析队友的设计思路,再结合自己实现时出现的难点和坑点进行实现的比较的过程,自己学习到了很多,比如复习了C++的一些相关函数,同时自己的思路也得到了开拓,对同一个问题的实现多样性有了比之前更浓厚的兴趣。很享受这种学习模式带来的成就感,感觉很smart。

原文地址:https://www.cnblogs.com/LiuZengyu/p/11552965.html

时间: 2024-10-07 02:10:50

中小学数学卷子自动生成程序-结对编程之队友代码分析的相关文章

中小学数学卷子自动生成程序: 结对编程对方代码分析

需求分析: 1.命令行输入用户名和密码,两者之间用空格隔开(程序预设小学.初中和高中各三个账号,具体见附表),如果用户名和密码都正确,将根据账户类型显示“当前选择为XX出题”,XX为小学.初中和高中三个选项中的一个.否则提示“请输入正确的用户名.密码”,重新输入用户名.密码: 2.登录后,系统提示“准备生成XX数学题目,请输入生成题目数量(输入-1将退出当前用户,重新登录):”,XX为小学.初中和高中三个选项中的一个,用户输入所需出的卷子的题目数量,系统默认将根据账号类型进行出题.每道题目的操作

中小学数学卷子自动生成 - 结对项目wjy的代码分析

[实现思路] 命令行输入用户名和密码,匹配试卷难度,每个类的账号只能生成当前难度下的试卷: 根据试卷难度生成题目,主要是随机生成操作数的个数,每个操作数的形式,两个操作符之间的操作符,最后是随机生成括号,因为设计题目的合理性,所以在写随机生成的时候需要考虑到各种可能的情况: 最后生成的试卷以“年-月-日-时-分-秒.txt”命名方式生成到以用户名命名的文件夹中. [函数结构] public static void main(String[] args) static void init() st

中小学数学卷子自动生成程序——结对分析

在上一周的个人项目中队友用的是C++,代码运行正常,且基本实现了需求文档的要求,然后关于她的代码,我觉得有很多可以借鉴的地方,也有一些需要改进的地方. 优点: 1.总体结构很清楚,思路清晰.从登录到功能切换,从试卷生成到题目生成,以及重复性检测,每一步都有对应的函数代码,思路清晰,逻辑清楚. 2.代码的编写基本符合规范要求,且注释基本到位,可读性强.例如在前面的主要函数声明中注释很清楚地标明清楚了函数的作用,函数基本命名符合规范,可以根据名字而知其意,变量命名也基本符合规范,这一点我得向她学习.

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

结对编程-队友代码分析 上一周,我们完成了个人项目编程.在经过对队友代码的分析,我感触颇深,觉得非常有必要来写一篇文章分析一下队友的代码,我是用的java,而队友用的c++,以下开始正文! ------------------------------------------------------------------------------- 项目需求简述: 个人项目:中小学数学卷子自动生成程序 用户: 小学.初中和高中数学老师. 功能: 1.命令行输入用户名和密码,两者之间用空格隔开(程序

中小学数学卷子自动生成程序分析

结对编程对方个人项目分析 个人项目:中小学数学卷子自动生成程序 用户:小学.初中和高中数学老师. 功能: 1.命令行输入用户名和密码,两者之间用空格隔开(程序预设小学.初中和高中各三个账号,具体见附表),如果用户名和密码都正确,将根据账户类型显示“当前选择为XX出题”,XX为小学.初中和高中三个选项中的一个.否则提示“请输入正确的用户名.密码”,重新输入用户名.密码: 2.登录后,系统提示“准备生成XX数学题目,请输入生成题目数量(输入-1将退出当前用户,重新登录):”,XX为小学.初中和高中三

中小学数学卷子自动生成程序分析 个人项目

每个人都弄了一个个人项目,然后结队,编程实现更多功能. 程序要求用户预设账号登录后可以选择自动生成不同数量(10-30道).不同难度(小学.初中.高中)的数学卷子到txt文本中. 通读项目需求,本次程序主要难点集中在题目严谨的数学逻辑与随机出题的结合以及出题不重复的实现. 下面简单分析一下李同学的工程编写优缺点: 优点: 文件路径使用相对路径.文件路径具有相对路径和绝对路径两种形式,而绝对路径可能会使得程序的可重用性差,使得在别的终端上不能运行,而在程序中使用相对路径则会变的有极强的适应性,包括

中小学数学试卷自动生成程序-对张凯翔同学代码的评价

张凯翔的优点: 面向对象编程,结构清晰,修改难度小 生成了UI界面,清晰美观 相比张惟盛的代码生成的题目更加科学,比如会在平方和开方的部分增加括号,以确保意义明确 相较于C语言,采用了更加方便的Java,调用函数以及捕获异常,确保了程度的稳定性 张凯旋的缺点: 代码习惯有点问题,杂乱且缺少注释 虽然采用了全随机生成运算符的方式,但在某些算式中也会出现无意义的括号,比如在正常的加减乘除中,第一个为乘法,已经是优先的但是还是会添加无意义的括号,有瑕疵 缺少了对同一老师的不同试卷进行题目查重的步骤,虽

结对编程:队友代码分析

按照结对编程要求,对队友的代码进行分析.在代码中发现的问题可以提醒队友或者警示自己. 优点: 一.代码以外: 1.使用语言为Python,比起JAVA和C++方便许多,而且实现许多功能也方便许多,可以说是从开始就为拓展打好了基础.事实上也的确因为这点,我们选用了他的代码作为核心 2.文件按功能分为几个文件,命名为CreatSymbol.CreatFirst.CreatSecond.CreatThird.Test.各个函数位置明确,命名规范 3.注释详细且明确.文件/函数的开头都有注释,各关键部分

结对编程——对队友代码分析

要实现的功能: 1.命令行输入用户名和密码,两者之间用空格隔开(程序预设小学.初中和高中各三个账号,具体见附表),如果用户名和密码都正确,将根据账户类型显示“当前选择为XX出题”,XX为小学.初中和高中三个选项中的一个.否则提示“请输入正确的用户名.密码”,重新输入用户名.密码: 2.登录后,系统提示“准备生成XX数学题目,请输入生成题目数量(输入-1将退出当前用户,重新登录):”,XX为小学.初中和高中三个选项中的一个,用户输入所需出的卷子的题目数量,系统默认将根据账号类型进行出题.每道题目的