结对编程-队友代码分析

Posted 1428173426wurui

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了结对编程-队友代码分析相关的知识,希望对你有一定的参考价值。

就在上一周中秋节期间,我们完成了个人项目,这一周就是结对编程了。显然,机智的我毫不犹豫地选择了我们帅气聪明的小姜做队友(抱紧大腿!).长这么大第一次写博客,还有很多功能都不会用,但是在经过对队友代码的分析,我感触颇深,觉得非常有必要来水一文!在自己写代码的时候,反正对自己还比较满意,我和小姜都是用的c++,但是经过对比才发现自己和别人的差距55.好了,接下来开始正文!

-----------------------------------------------------------------------------------------------------------------------------

项目需求简述:

项目名称:中小学数学卷子自动生成程序

用户:小学、初中和高中数学老师。

功能:

1、命令行输入用户名和密码,两者之间用空格隔开(程序预设小学、初中和高中各三个账号,具体见附表),如果用户名和密码都正确,将根据账户类型显示“当前选择为XX出题”,XX为小学、初中和高中三个选项中的一个。否则提示“请输入正确的用户名、密码”,重新输入用户名、密码;

2、登录后,系统提示“准备生成XX数学题目,请输入生成题目数量:”,XX为小学、初中和高中三个选项中的一个,用户输入所需出的卷子的题目数量,系统默认将根据账号类型进行出题。每道题目的操作数在1-5个之间,操作数取值范围为1-100;

3、题目数量的有效输入范围是“10-30”(含10,30),程序根据输入的题目数量生成符合小学、初中和高中难度的题目的卷子(具体要求见附表)。同一个老师的卷子中的题目不能与以前的已生成的卷子中的题目重复(以指定文件夹下存在的文件为准,见5);

4、在登录状态下,如果用户需要切换类型选项,命令行输入“切换为XX”,XX为小学、初中和高中三个选项中的一个,输入项不符合要求时,程序控制台提示“请输入小学、初中和高中三个选项中的一个”;输入正确后,显示“”系统提示“准备生成XX数学题目,请输入生成题目数量”,用户输入所需出的卷子的题目数量,系统新设置的类型进行出题;

5、生成的题目将以“年-月-日-时-分-秒.txt”的形式保存,每个账号一个文件夹。每道题目有题号,每题之间空一行;

 

下面就开始对队友的代码进行简要分析:

1.代码模块结构非常清晰,层次明显。

队友把整个工程分为几个模块,大致是用户登录(其中确定试卷类型),获取操作数值,获取系统时间,获取操作符,生成单个问题表达式,生成试卷,切换试卷类型,主函数。这些都用相互比较独立的函数实现,基本吻合“高内聚,低耦合”。结构逻辑非常清晰,通俗易懂。作为对比,我的结构就是相当杂糅,而且把登录等功能块直接杂在main函数中,造成结构冗杂,很不雅观。

2.代码性能好,效率比较高

在仔细品味队友代码的过程中,一些小细节让我感触很深。比如下面单个问题生成模块中,针对不同操作数,队友采用了switch语句,如果是我首先想到的肯定是if-else语句。switch...case会生成一个跳转表来指示实际的case分支的地址,而这个跳转表的索引号与switch变量的值是相等的。从而,switch...case不用像if...else那样遍历条件分支直到命中条件,而只需访问对应索引号的表项从而到达定位分支的目的。显然,switch效率就比if-else高,这种小细节的处理让我感到惊艳。代码的性能在这些细节上直接拉开了差距,也是我需要学习的另一个地方。

switch(OpeNum)
	{
		case 1: //一个操作不用括号 
			Que=GetNumber(type)+GetOperator()+GetNumber(type);
			break;
			
		case 2: //两个操作时的三种随机情况 a+b+c (a+b)+c  a+(b+c) 
			temp=rand()%3;
			switch(temp)
			{
				case 0:
					Que="("+GetNumber(type)+GetOperator()+GetNumber(type)+")"+GetOperator()+GetNumber(type);
					break;
				case 1:
					Que=GetNumber(type)+GetOperator()+"("+GetNumber(type)+GetOperator()+GetNumber(type)+")";
					break;
				case 2:
					Que=GetNumber(type)+GetOperator()+GetNumber(type)+GetOperator()+GetNumber(type);
					break;
			}
			break;
			
		default: //三个操作及以上情况 
			for(int i=0;i<OpeNum;i++)
			{
				temp=rand()%3;
				if(!temp&&leftnum==0) // 1/3的概论产生左括号 
				{
					Que+="(";
					leftnum++;
					leftpos=i;
				}
				
				temp=rand()%2; // 1/2的概率补充右括号,以及防止出现(a+b+c+d)情况出现 
				if((!temp&&leftnum!=0&&(i>leftpos))||(leftnum!=0&&i==OpeNum-1&&leftpos==0))
				{
					Que+=GetNumber(type)+")"+GetOperator();
					leftnum--;
				}
				else
					Que+=GetNumber(type)+GetOperator();
				
				
				if(i==OpeNum-1&&leftnum!=0) //最后补充一个随机数字,同时防止出现没有右括号的情况 
					Que+=GetNumber(type)+")";
				else if(i==OpeNum-1)
					Que+=GetNumber(type);
			}
			break;
	}

3.部分设计构思巧妙,让人眼前一亮

队友在生成题目的模块,巧妙地设计了一个题库,既能为后面检查是否有重复题目做准备,而且这个题库我们可以为后续结对编程开发项目做准备-我们可以从题库中出题!这直接提供了一个好的思路,队友巧妙的构思让我非常钦佩!

	for(int i=0;i<QueNum;i++)//创建试卷 
	{
		int OpeNum=rand()%5+1; //随机产生>1和<5的操作数个数 
		string Que=QuestionCreate(OpeNum,g_type);
		string s;
		int flag=0;
		while(getline(ifile,s)) //在总题库中查重 
		{
			if(s==Que)
				flag=1;
		}
		if(flag==0)
		{
			ofile1<<i+1<<". "<<Que<<"="<<endl<<endl;  //输入到对应TXT 
			ofile2<<Que<<endl; 	//输入到总题库 
		}
		else
		{
			i--;
		}
	}

 

4.注释详尽,益于读者理解

队友在各个代码块都有进行注释,这是一个良好的习惯。对于读者来说,注释能让人快速准确的理解作者所要表达的意思。对于作者,注释也能让自己的逻辑更加清晰~而我QAQ,忘记写注释了(其实之前写了,写到一半嫌太麻烦又删了)

-----------------------------------------------------------------------------------------------------------------------------

队友的代码还有很多很多优点,由于篇幅原因就不再赘述。但是整个设计也有一点点小问题~

1.括号的问题

队友对于产生括号的思路是:设置左括号数量和左括号位置的变量,然后再后续匹配右括号。但是这种方法一个不足是忽略了可能有连续两个左括号的情况。

 //三个操作及以上情况 
			for(int i=0;i<OpeNum;i++)
			{
				temp=rand()%3;
				if(!temp&&leftnum==0) // 1/3的概论产生左括号 
				{
					Que+="(";
					leftnum++;
					leftpos=i;
				}
				
				temp=rand()%2; // 1/2的概率补充右括号,以及防止出现(a+b+c+d)情况出现 
				if((!temp&&leftnum!=0&&(i>leftpos))||(leftnum!=0&&i==OpeNum-1&&leftpos==0))
				{
					Que+=GetNumber(type)+")"+GetOperator();
					leftnum--;
				}
				else
					Que+=GetNumber(type)+GetOperator();
				
				
				if(i==OpeNum-1&&leftnum!=0) //最后补充一个随机数字,同时防止出现没有右括号的情况 
					Que+=GetNumber(type)+")";
				else if(i==OpeNum-1)
					Que+=GetNumber(type);
			}
			break;
	}

 

我的思路是:对于第一个和最后一个操作数,先不加括号,统计中间操作数的左括号和右括号数,对于左括号我们可以直接在最后一个操作数后面补右括号。对于左括号,我们可以在第一个操作数前面加左括号。因为操作数最多只有五个,所以中间操作数最多只有三个,基本不会出现括号冗余的现象,而且也包含了所有情况。代码写的太丑了,就不贴了。

2.还能做一点点小小的优化

队友的代码基本上非常简洁,优化做的非常棒。但是有一个小地方有一点点我可以改的地方。比如他下面这个产生操作符的函数,我觉得可以直接用一个字符数组代替

string GetOperator() //获取随机操作
{
    string Ope;
    int temp=rand()%4;
    if(temp==0)
        Ope="+";
    else if(temp==1)
        Ope="-";
    else if(temp==2)
        Ope="*";
    else if(temp==3)
        Ope="/";
    return Ope;
}

 ----------------------------------------------------------------------------------------------------------------------------

总而言之,队友的代码写的非常棒,与我的烂代码形成鲜明对比。我的代码重复性高,模块杂糅,没有注释,还缺少查重功能的实现,简直灾难。队友的代码让我感触很深,而且他用了很短很短时间就写完了。显然,我的编程水平和他的差距十分明显,在接下来的学习中,我会虚心向他学习,尽量改正自己编程过程中的坏毛病。

好了,第一次博客差不多就写这么多吧,接下来要加油了~

以上是关于结对编程-队友代码分析的主要内容,如果未能解决你的问题,请参考以下文章

结对编程-队友代码分析

结对编程队友代码分析

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

结对编程-队友代码分析

结对编程队友代码分析

结对编程之队友代码分析