结对编程分析:中小学数学卷子自动生成程序
结对编程评价博客
上一周,我们完成了个人项目编程。在经过对队友代码的分析,我感触颇深,觉得非常有必要来写一篇文章分析一下队友的代码,我是用的c++,而队友用的java,以下开始正文!
-------------------------------------------------------------------------------
项目需求简述:
个人项目:中小学数学卷子自动生成程序
用户:
小学、初中和高中数学老师。
功能:
1、命令行输入用户名和密码,两者之间用空格隔开(程序预设小学、初中和高中各三个账号,具体见附表),如果用户名和密码都正确,将根据账户类型显示“当前选择为XX出题”,XX为小学、初中和高中三个选项中的一个。否则提示“请输入正确的用户名、密码”,重新输入用户名、密码;
2、登录后,系统提示“准备生成XX数学题目,请输入生成题目数量(输入-1将退出当前用户,重新登录):”,XX为小学、初中和高中三个选项中的一个,用户输入所需出的卷子的题目数量,系统默认将根据账号类型进行出题。每道题目的操作数在1-5个之间,操作数取值范围为1-100;
3、题目数量的有效输入范围是“10-30”(含10,30,或-1退出登录),程序根据输入的题目数量生成符合小学、初中和高中难度的题目的卷子(具体要求见附表)。同一个老师的卷子中的题目不能与以前的已生成的卷子中的题目重复(以指定文件夹下存在的文件为准,见5);
4、在登录状态下,如果用户需要切换类型选项,命令行输入“切换为XX”,XX为小学、初中和高中三个选项中的一个,输入项不符合要求时,程序控制台提示“请输入小学、初中和高中三个选项中的一个”;输入正确后,显示“”系统提示“准备生成XX数学题目,请输入生成题目数量”,用户输入所需出的卷子的题目数量,系统新设置的类型进行出题;
5、生成的题目将以“年-月-日-时-分-秒.txt”的形式保存,每个账号一个文件夹。每道题目有题号,每题之间空一行;
6、个人项目9月17日早上8点以前提交至各自负责的助教处。提交方式:工程文件打包,压缩包名为“几班+姓名.rar”。
附表-1:账户、密码
账户类型 |
账户 |
密码 |
备注 |
小学 |
张三1 |
123 |
|
张三2 |
123 |
|
|
张三3 |
123 |
|
|
初中 |
李四1 |
123 |
|
李四2 |
123 |
|
|
李四3 |
123 |
|
|
高中 |
王五1 |
123 |
|
王五2 |
123 |
|
|
王五3 |
123 |
|
附表-2:小学、初中、高中题目难度要求
|
小学 |
初中 |
高中 |
|
难度要求 |
+,-,*./ |
平方,开根号 |
sin,cos,tan |
|
备注 |
只能有+,-,*./和() |
题目中至少有一个平方或开根号的运算符 |
题目中至少有一个sin,cos或tan的运算符 |
|
-------------------------------------------------------------------------------
需求分析:
根据项目需求,需要设计一个能够根据账号登录并按不同账号等级随机出题,并将生成的题目生成txt文本形成卷子。项目需要用到文件IO,字符串增删改查,随机化等等知识。
-------------------------------------------------------------------------------
下面就开始对队友的代码进行简要分析
代码分析:
队友的代码有不少优点:
- 注释比较到位:
分支循环等等程序流程发生改变的地方都做了注释,保证了代码基本的可读性,能够通过分支循环语句等了解大体的程序结构。结构大体上比较清晰。
代码如下:
public static StringBuffer createproblem(int number) //创建单个题目 number为操作数
{
StringBuffer problem = new StringBuffer(); //单个题目以StringBuffer储存
switch(g_grade) //选择学历
{
case 0: //小学题目
{
for(int i=0;i<number;i++)
{
if(i==0)
{
problem.append(rdm.nextInt(100)+1); //第一个操作数之前无符号
}
else //之后的操作数需要随机符号
{
problem.append(g_oprtsg[rdm.nextInt(4)]);
problem.append((rdm.nextInt(100)+1));
}
}
}
break;
case 1: //初中题目
{
for(int i=0;i<number;i++)
{
if(i==0) //第一个操作数开方或者平方 保证符合要求
{
switch (rdm.nextInt(2))
{
case 0:
{
problem.append("(");
problem.append((rdm.nextInt(100)+1));
problem.append("^2");
problem.append(")");
}
break;
default:
{
problem.append(g_oprtsg[4]);
problem.append("(");
problem.append((rdm.nextInt(100)+1));
problem.append(")");
}
break;
}
}
else //之后的操作数随机 可能有开方或者平方也可能没有
{
int flag=rdm.nextInt(6);
switch(flag)
{
case 0:case 1:case 2:case 3:
{
problem.append(g_oprtsg[flag]);
problem.append((rdm.nextInt(100)+1));
}
break;
case 4:
{
problem.append(g_oprtsg[rdm.nextInt(4)]);
problem.append("(");
problem.append((rdm.nextInt(100)+1));
problem.append(g_oprtsg[flag]);
problem.append(")");
}
break;
case 5:
{
problem.append(g_oprtsg[rdm.nextInt(4)]);
problem.append(g_oprtsg[flag]);
problem.append("(");
problem.append((rdm.nextInt(100)+1));
problem.append(")");
}
break;
default:System.out.println("create problem error!");break;
}
}
}
}
break;
case 2: //高中题目
{
for(int i=0;i<number;i++) //第一个操作数做三角函数运算 保证符合要求
{
if(i==0)
{
problem.append(g_oprtsg[rdm.nextInt(3)+6]);
problem.append("(");
problem.append((rdm.nextInt(100)+1));
problem.append(")");
}
else //之后随机 可能有高级运算也可能没有
{
int flag=rdm.nextInt(9);
switch(flag)
{
case 0:case 1:case 2:case 3:
{
problem.append(g_oprtsg[flag]);
problem.append((rdm.nextInt(100)+1));
}
break;
case 4:
{
problem.append(g_oprtsg[rdm.nextInt(4)]);
problem.append("(");
problem.append((rdm.nextInt(100)+1));
problem.append(g_oprtsg[flag]);
problem.append(")");
}
break;
case 5:
{
problem.append(g_oprtsg[rdm.nextInt(4)]);
problem.append(g_oprtsg[flag]);
problem.append("(");
problem.append((rdm.nextInt(100)+1));
problem.append(")");
}
break;
case 6:case 7:case 8:
{
problem.append(g_oprtsg[rdm.nextInt(4)]);
problem.append(g_oprtsg[flag]);
problem.append("(");
problem.append((rdm.nextInt(100)+1));
problem.append(")");
}
break;
default:System.out.println("create problem error!");
}
}
}
}
break;
default:System.out.println("create problem error!");
}
return problem;
}
- 函数调用比较合理:
模块划分比较清晰:生成题目查重后将题目添加到试卷中。将整体划分为三个部分,生成题目,合并为卷子,生成文本,结构划分清晰合理。
比如
public static List <StringBuffer> createpaper(int amount) //创建试卷
{
List <StringBuffer> paper = new ArrayList<>(); //将试卷储存在List容器中
for(int i=0;i<amount;i++)
{
StringBuffer problem = createproblem(rdm.nextInt(5)+1); //创建单个题目
for(int j=0;j<i;j++)
{
if(problem.equals(paper.get(j))) //如果与前面的题目重复则重新随机出题
{
problem = createproblem(rdm.nextInt(5)+1);
j=0; //改变计数器 重新比对
}
}
paper.add(problem); //无重复 写入试卷
}
return paper;
}
但也有一些缺点:
- 命令行匹配字符串设计有改进空间:
命令行切换等级的方法既然分出了一个模块,就不应该在主函数再用分支语句判断。这造成了代码的冗杂,减慢了代码执行的速度。
case 0:
{
switch (cmd) //切换学历
{
case "切换为小学":
{
g_grade = 0;
System.out.println("系统提示:准备生成小学数学题目,请输入生成题目数量");
}
break;
case "切换为初中":
{
g_grade = 1;
System.out.println("系统提示:准备生成初中数学题目,请输入生成题目数量");
}
break;
- 有些方法的编写重复性太多:
随机生成题目的方法在不同等级的代码中有交叉的部分,可以考虑将整个方法打散再归并优化。
public static void writeFile(String pathname , String text) //写文件
{
try
{
File writeName = new File(pathname); //相对路径 如果没有则要建立一个新的txt文件
File dir = writeName.getParentFile(); //得到文件的父目录
if (!dir.exists()) //父目录不存在则建立一个
{
dir.mkdirs();
}
try
{
writeName.createNewFile(); //创建新文件,有同名的文件的话直接覆盖
}
catch (IOException e)
{
e.printStackTrace();
}
try
(
FileWriter writer = new FileWriter(writeName);
BufferedWriter out = new BufferedWriter(writer)
)
{
out.write(text);
out.flush(); //把缓存区内容压入文件
}
}
catch (IOException e)
{
e.printStackTrace();
}
}
-------------------------------------------------------------------------------
总结:
整体来说工程基本满足了项目需求,能够合理的完成任务,就是有一些小问题,我们会在接下来的结对项目中进行改进,写出更好的代码。