结对项目-四则运算
这个作业属于哪个课程 | 计科22级12班 |
---|---|
这个作业要求在哪里 | 作业 |
这个作业的目标 | 与队友合作生成一个能够实现小学四则运算的程序 |
Github链接
一、队员信息
柯锦宏 | 3122004910 |
---|---|
陈煜 | 3122004904 |
2、PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 720 | 545 |
· Estimate | · 估计这个任务需要多少时间 | 720 | 540 |
Development | 开发 | 500 | 400 |
· Analysis | · 需求分析 (包括学习新技术) | 120 | 60 |
· Design Spec | · 生成设计文档 | 60 | 60 |
· Design Review | · 设计复审 | 30 | 15 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 30 | 30 |
· Design | · 具体设计 | 120 | 120 |
· Coding | · 具体编码 | 60 | 40 |
· Code Review | · 代码复审 | 30 | 10 |
· Test | · 测试(自我测试,修改代码,提交修改) | 60 | 30 |
Reporting | 报告 | 30 | 30 |
· Test Repor | · 测试报告 | 60 | 60 |
· Size Measurement | · 计算工作量 | 60 | 60 |
Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 60 | 30 |
· 合计 | 720 | 545 |
3模块设计
3.1、设计实现过程
AnswerValidator类:
读取题目和用户的答案文件,并比较它们,确定每道题的答案是正确还是错误,然后将结果写入成绩文件。
- validateAnswers()方法:用于处理答案验证的逻辑。
ProducerController类:
根据用户输入的设置生成指定数量的四则运算题目(包括整数和分数),并将题目和答案输出到两个文本文件中。
- inputProblemSettings()方法:用于接收用户输入并进行验证。
- generateArithmeticProblem(int numberOfProblems, int maxRange)方法:生成题目并输出到文件。
CreateFraction类:
生成包含加法和减法的真分数表达式,并计算出对应的结果。
- createFractionProblem(int range):生成真分数表达式和结果。
CreateInteger类:
生成包含加法、减法、乘法和除法的随机整数表达式,并计算出结果。
-输入-:一个整数 range,用于限定生成的随机数范围。
-输出-:一个字符串数组,包含生成的数学表达式和计算结果。
-
createIntegerProblem(int range):生成数学表达式和计算结果。
-
indexOfOperator(int operatorCount, int operatorTypes, Random random):生成随机操作符的下标。
-
stitchingFormula(int operatorCount, int[] operand, int[] operatorIndex):拼接数学表达式。
-
algorithm(String s):计算给定表达式的值。
-
performSingleOperation(int a, int b, String operator):执行单个操作符的运算。
FileUtil类:
为一个测验或考试应用提供基本的方法,管理练习题和答案,记录成绩,并有效地输出结果。它强调可读性和可维护性,同时确保文件操作的基本错误处理。
-
readExerciseFile(String path):读取格式为 "题目=答案" 的题目文件。
-
readAnswerFile(String path):读取格式为 "题目 答案" 的答案文件。
-
writeGradeFile(List
correct, List :将正确和错误的答案结果写入名为 "Grade.txt" 的文件。wrong) -
outputFile(int index, String[] problem, PrintStream... streams):将题目和对应的答案输出到两个提供的 PrintStream 对象(例如控制台、文件)。
Math类:
分别用于计算最大公因数、生成互质数对以及将假分数转换为真分数。
-
greatFactor_GCD(int x, int y):使用辗转相除法(欧几里得算法)计算两个整数 x 和 y 的最大公因数(GCD)。
-
createCoprimeNumbers(int range, Random random):生成一对互质数(即最大公因数为 1 的两个数)。
-
shamToProperFraction(int x, int y):将假分数(分子大于分母的分数)转换为真分数或带分数形式。
3.2、类的设计流程图:
MODEL类设计:
Controller类设计:
Util类设计:
4代码具体实现
- AnswerValidator类
点击查看代码
public class AnswerValidator {
// 启动方法,开始验证答案的过程
public void validateAnswers() {
FileUtil fileUtil = new FileUtil();
Scanner scanner = new Scanner(System.in);
// 提示用户输入题目文件路径
System.out.print("请输入题目文件路径:");
String exerciseFilePath = scanner.next();
// 提示用户输入待验证答案文件路径
System.out.print("请输入待验证答案文件路径:");
String answerFilePath = scanner.next();
try {
// 读取题目文件和答案文件
List<String> exercises = fileUtil.readExerciseFile(exerciseFilePath);
List<String> answers = fileUtil.readAnswerFile(answerFilePath);
// 存储正确和错误的题目编号
List<String> correctAnswers = new ArrayList<>();
List<String> incorrectAnswers = new ArrayList<>();
// 获取最小答案数量以避免越界
int min = Math.min(exercises.size(), answers.size());
int num = 1;
for (int i = 0; i < min; i++){
if (exercises.get(i).equals(answers.get(i))){
correctAnswers.add(String.valueOf(num++));
}else {
incorrectAnswers.add(String.valueOf(num++));
}
}
// 写入成绩文件
fileUtil.writeGradeFile(correctAnswers, incorrectAnswers);
// 完成判定后输出信息
System.out.println("判定完成");
} catch (FileNotFoundException e) {
System.out.println("文件不存在");
} catch (IOException e) {
System.out.println("文件读入异常");
}
}
}
- ProducerController类
点击查看代码
public class ProducerController {
/**
* 提示用户生成题目
* @throws InputMismatchException 输入匹配错误异常
*/
public void inputProblemSettings () {
System.out.println("四则运算生成器生成题目\n");
Scanner scanner = null;
try {
scanner = new Scanner(System.in);
System.out.print("请输入生成题目个数:"); // 输入题目个数
int numberOfProblems = scanner.nextInt();
if (numberOfProblems <= 0) {
System.out.println("生成题目个数必须为正整数。\n");
inputProblemSettings (); // 重新调用以获取有效输入
return;
}
System.out.print("请输入最大自然数:"); // 输入自然数范围
int maxRange = scanner.nextInt();
if (maxRange <= 0) {
System.out.println("最大自然数必须为正整数。\n");
inputProblemSettings (); // 重新调用以获取有效输入
return;
}
generateArithmeticProblem(numberOfProblems , maxRange); // 生成题目
} catch (InputMismatchException e) {
System.out.println("输入数据错误,请输入数字。\n\n\n"); // 输入格式错误提示
if (scanner != null) {
scanner.nextLine(); // 清除错误输入
}
inputProblemSettings (); // 重新调用以获取有效输入
} catch (IOException e) {
System.out.println("文件创建失败"); // 文件创建异常处理
}
}
/**
* 生成并输出题目到文件
* @param numberOfProblems 题目个数
* @param maxRange 最大自然数范围
* @throws IOException 文件异常
*/
public void generateArithmeticProblem(int numberOfProblems, int maxRange) throws IOException {
FileUtil fileUtil = new FileUtil();
// 创建题目和答案文件
File exercisesFile = new File("Exercises.txt");
File answersFile = new File("Answers.txt");
// 删除已存在的文件
if (exercisesFile.exists()){
boolean flag = exercisesFile.delete(); // 删除已有文件
if (!flag) {
System.out.println("无法删除旧的题目文件"); // 删除失败时输出提示
}
}
if (answersFile.exists()){
boolean flag = answersFile.delete(); // 删除已有文件
if (!flag) {
System.out.println("无法删除旧的题目文件"); // 删除失败时输出提示
}
}
// 创建新文件
try (PrintStream exercisesPrintStream = new PrintStream(Files.newOutputStream(exercisesFile.toPath()));
PrintStream answersPrintStream = new PrintStream(Files.newOutputStream(answersFile.toPath()))) {
Random random = new Random(); // 随机数生成器
CreateFraction createFraction = new CreateFraction(); // 分数题目生成器
CreateInteger createInteger = new CreateInteger(); // 整数题目生成器
for (int i = 1; i <= numberOfProblems; i++) {
boolean isFraction = random.nextBoolean(); // 返回true或false
String[] expressionAndAns;
if (isFraction) {
expressionAndAns = createFraction.createFractionProblem(maxRange); // 生成分数题目
} else {
expressionAndAns = createInteger.createIntegerProblem(maxRange); // 生成整数题目
}
fileUtil.outputFile(i, expressionAndAns, exercisesPrintStream, answersPrintStream); // 输出题目和答案
}
System.out.println("题目和与之对应的答案文件创建成功。"); // 成功提示
}
}
}
- CreateFraction类
点击查看代码
public class CreateFraction {
private static final String[] OPERATOR = {"+", "-"}; // 操作符数组
/**
* 真分数公式与答案生成器,分为加法与减法
* @param maxRange 范围,用于生成随机数
* @return 包含生成的数学表达式和结果的字符串数组
*/
public String[] createFractionProblem(int maxRange) {
Math math = new Math(); // 创建数学工具类实例
Random random = new Random(); // 创建随机数生成器
int operatorCount = 1 + random.nextInt(3); // 操作符的个数为1到3
CreateInteger create = new CreateInteger(); // 创建整数生成器实例
int[] operatorIndex = create.indexOfOperator(operatorCount, 2, random); // 获取操作符的下标
// 生成第一个操作数
int[] coprimeNumber1 = math.createCoprimeNumbers(maxRange, random); // 生成互质数
int x = coprimeNumber1[0]; // 分子
int y = coprimeNumber1[1]; // 分母
// 将假分数转化为真分数字符串
StringBuilder expression = new StringBuilder(math.shamToProperFraction(x, y));
// 循环生成剩下的操作数
for (int i = 0; i < operatorCount; i++) {
// 生成剩下的操作数
int[] coprimeNumber2 = math.createCoprimeNumbers(maxRange, random); // 生成新的互质数
int num_x = coprimeNumber2[0]; // 新的分子
int num_y = coprimeNumber2[1]; // 新的分母
String currentOperator = OPERATOR[operatorIndex[i]]; // 获取当前操作符
// 处理加法
if (currentOperator.equals("+")) {
// 分母相乘,分子相加
x = x * num_y + y * num_x;
}
else { // 处理减法
int count = 0; // 用于防止无限循环的计数器
// 确保差为非负数
while (x * num_y - y * num_x < 0) {
// 重新生成互质数
coprimeNumber2 = math.createCoprimeNumbers(maxRange, random);
num_x = coprimeNumber2[0]; // 更新新的分子
num_y = coprimeNumber2[1]; // 更新新的分母
count++;
if (count >= 10) { // 防止无限循环
num_x = x - 1; // 设置一个默认值
num_y = y; // 保持分母不变
}
}
// 分母相乘,分子相减
x = x * num_y - y * num_x;
}
// 更新分母
y = y * num_y;
// 转换当前操作数为真分数字符串并拼接表达式
String num = math.shamToProperFraction(num_x, num_y);
expression.append(currentOperator).append(num);
}
// 计算最大公因数以简化结果
int gcd = math.greatFactor_GCD(x, y);
if (gcd < 0) {
return createFractionProblem(maxRange); // 返回-1,让生成重新生成
}
// 化简结果
x /= gcd;
y /= gcd;
// 将最终结果转化为真分数字符串并拼接等号
String ans = math.shamToProperFraction(x, y);
expression.append("=");
// 返回生成的表达式和结果
return new String[]{expression.toString(), ans};
}
}
- CreateInteger类
点击查看代码
public class CreateInteger {
private static final String[] OPERATOR = {"+", "-", "*", "÷"}; // 操作符数组
/**
* 整数生成器
* @param range 范围,用于生成随机数
* @return 包含生成的数学表达式和结果的字符串数组
*/
public String[] createIntegerProblem(int range) {
Random random = new Random();
int operatorCount = 1 + random.nextInt(3); // 随机生成操作符的个数(1到3个)
int operandCount = operatorCount + 1; // 操作数的数量
int[] operand = new int[operandCount]; // 存储操作数
// 获取操作符的下标数组
int[] operatorIndex = indexOfOperator(operatorCount, 4, random);
// 随机生成操作数
for (int i = 0; i < operandCount; i++) {
operand[i] = random.nextInt(range); // 生成范围内的随机数
}
String problem = stitchingProblem(operatorCount, operand, operatorIndex); // 拼接公式
int ans = calculator(problem); // 使用 calculator方法 计算结果
// 如果结果有效,则返回公式和结果;否则重新生成问题
if (ans > 0) {
return new String[]{problem.toString(), String.valueOf(ans)}; // 返回公式和结果
} else {
return createIntegerProblem(range); // 递归调用以重新生成问题
}
}
/**
* 随机产生操作符的对应的字符数组的下标
* @param operatorCount 操作符的数量
* @param operatorTypes 操作符种类数量
* @param random 随机数生成器
* @return 随机生成的操作符下标数组
*/
public int[] indexOfOperator(int operatorCount, int operatorTypes, Random random) {
int[] operatorIndex = new int[operatorCount]; // 存储操作符下标
// 随机生成操作符下标
for (int i = 0; i < operatorCount; i++) {
operatorIndex[i] = random.nextInt(operatorTypes); // 获取随机下标
}
return operatorIndex; // 返回生成的下标数组
}
/**
* 拼接数学表达式
* @param operatorCount 操作符数量
* @param operand 操作数数组
* @param operatorIndex 操作符下标数组
* @return 拼接后的数学表达式字符串
*/
public String stitchingProblem(int operatorCount, int[] operand, int[] operatorIndex) {
int bracketForm = new Random().nextInt(2); // 随机决定是否使用括号
StringBuilder expression = new StringBuilder(); // 用于拼接公式
// 根据操作符数量拼接表达式
switch (operatorCount) {
case 1:// a+b型
expression.append(String.format("%s %s %s =", operand[0], OPERATOR[operatorIndex[0]], operand[1]));
break;
case 2: {// a+b+c 型
if (bracketForm == 0) {
expression.append(String.format("%s %s %s %s %s =",operand[0],OPERATOR[operatorIndex[0]],
operand[1],OPERATOR[operatorIndex[1]],operand[2]));
} else {//a+(b+c)型
expression.append(String.format("%s %s ( %s %s %s ) =",operand[0],OPERATOR[operatorIndex[0]],
operand[1],OPERATOR[operatorIndex[1]],operand[2]));
}
break;
}
case 3: {//a+((b+c)-d)型
if (bracketForm == 0) {
expression.append(String.format("%s %s (( %s %s %s ) %s %s) =",operand[0],OPERATOR[operatorIndex[0]],
operand[1],OPERATOR[operatorIndex[1]],operand[2],OPERATOR[operatorIndex[2]],operand[3]));
}
else {//(a+b)+(c+d)型
expression.append(String.format("( %s %s %s ) %s ( %s %s %s ) =",operand[0],OPERATOR[operatorIndex[0]],
operand[1],OPERATOR[operatorIndex[1]],operand[2],OPERATOR[operatorIndex[2]],operand[3]));
}
break;
}//可添加更多类型,如a+b+c+d型,a+(b+c)-d型等等
}
return expression.toString(); // 返回拼接的数学表达式
}
/**
* 计算给定表达式的值
* @param s 输入的数学表达式字符串
* @return 计算结果,返回整数值
*/
public int calculator(String s) {
Stack<Integer> numStack = new Stack<>(); // 用于存放数字的栈
Stack<String> operatorStack = new Stack<>(); // 用于存放操作符的栈
HashMap<String, Integer> hashMap = new HashMap<>(); // 存放运算符优先级的哈希表
hashMap.put("(", 0); // 左括号优先级最低
hashMap.put("+", 1); // 加法优先级
hashMap.put("-", 1); // 减法优先级
hashMap.put("*", 2); // 乘法优先级
hashMap.put("÷", 2); // 除法优先级
String formula = s.replaceAll(" ", ""); // 去除输入字符串中的空格
for (int i = 0; i < formula.length();) {
StringBuilder digit = new StringBuilder(); // 当前数字的StringBuilder
char c = formula.charAt(i); // 获取当前字符
// 处理数字,收集连续的数字字符
while (Character.isDigit(c)) { // 判断当前字符是否为数字
digit.append(c); // 将数字字符加入digit
i++;
if (i < formula.length()) {
c = formula.charAt(i); // 获取下一个字符
} else {
break; // 结束循环
}
}
// 处理操作符
if (digit.length() == 0) { // 如果没有数字,说明当前是操作符
switch (c) {
case '(': {
operatorStack.push(String.valueOf(c)); // 压入左括号
break;
}
case ')': { // 处理右括号
String stmp = operatorStack.pop(); // 弹出操作符栈顶元素
while (!operatorStack.isEmpty() && !stmp.equals("(")) { // 计算直到遇到左括号
int a = numStack.pop(); // 弹出操作数a
int b = numStack.pop(); // 弹出操作数b
int result = performSingleOperation(b, a, stmp); // 进行计算
if (result < 0) return -1; // 错误处理
numStack.push(result); // 将结果压入数字栈
stmp = operatorStack.pop(); // 更新操作符
}
break;
}
case '=': { // 处理等号
String stmp;
while (!operatorStack.isEmpty()) { // 计算所有剩余操作符
stmp = operatorStack.pop(); // 弹出操作符
int a = numStack.pop(); // 弹出操作数a
int b = numStack.pop(); // 弹出操作数b
int result = performSingleOperation(b, a, stmp); // 进行计算
if (result < 0) return -1; // 错误处理
numStack.push(result); // 将结果压入数字栈
}
break;
}
default: { // 处理其他操作符
String stmp;
while (!operatorStack.isEmpty()) { // 当符号栈非空时
stmp = operatorStack.pop(); // 获取栈顶操作符
// 比较优先级
if (hashMap.get(stmp) >= hashMap.get(String.valueOf(c))) {
int a = numStack.pop(); // 弹出操作数a
int b = numStack.pop(); // 弹出操作数b
int result = performSingleOperation(b, a, stmp); // 进行计算
if (result < 0) return -1; // 错误处理
numStack.push(result); // 将结果压入数字栈
} else {
operatorStack.push(stmp); // 优先级高的操作符回压
break; // 退出循环
}
}
operatorStack.push(String.valueOf(c)); // 压入当前操作符
break;
}
}
} else { // 处理数字
numStack.push(Integer.valueOf(digit.toString())); // 将数字压入数字栈
continue; // 结束本次循环,回到for语句进行下一次循环
}
i++; // 移动到下一个字符
}
return numStack.peek(); // 返回栈顶的数字,即表达式的计算结果
}
/**
* 执行单个操作的计算
* @param a 操作数1
* @param b 操作数2
* @param operator 操作符
* @return 计算结果
*/
public int performSingleOperation(int a, int b, String operator) {
switch (operator) {
case "+":
return a + b; // 加法
case "-":
return a - b; // 减法
case "*":
return a * b; // 乘法
case "÷":
if (b == 0) return -1; // 除数为零时返回错误
return a / b; // 除法
default:
return -1; // 返回错误
}
}
}
- FileUtil类
点击查看代码
public class FileUtil {
// 读取题目文件
public List<String> readExerciseFile(String path) throws IOException {
BufferedReader exerciseReader = new BufferedReader(new FileReader(path));
String exercise;
List<String> exercises = new ArrayList<>();
// 按行读取文件内容
while ((exercise = exerciseReader.readLine()) != null) {
String[] split = exercise.split("=");
//String[] split = exercise.split("[=.]");
if (split.length >= 2) {
exercises.add(split[1].trim()); // 提取并去除空白
//System.out.println(split[1].trim());
} else {
exercises.add(" "); // 如果格式不对,添加空格
System.out.println(split[0]+"答案未输入或写入文件的题目格式错误。");
}
}
return exercises; // 返回题目列表
}
// 读取答案文件
public List<String> readAnswerFile(String path) throws IOException {
BufferedReader answerReader = new BufferedReader(new FileReader(path));
String answer;
List<String> answers = new ArrayList<>();
// 按行读取文件内容
while ((answer = answerReader.readLine()) != null) {
String[] split = answer.split(" ");
if (split.length >= 2) {
answers.add(split[1].trim()); // 提取并去除空白
//System.out.println(split[1].trim());
//trim() 方法用于去掉字符串两端的空格
} else {
answers.add(" "); // 如果格式不对,添加空格
System.out.println(split[0]+"写入文件的答案格式错误。");
}
}
return answers; // 返回答案列表
}
// 写入成绩文件
public void writeGradeFile(List<String> correct, List<String> wrong) throws IOException {
File grade = new File("Grade.txt");
if (grade.exists()) {
boolean flag = grade.delete(); // 删除已有文件
if (!flag) {
System.out.println("无法删除成绩文件"); // 删除失败时输出提示
}
}
if (grade.createNewFile()) {
try (FileOutputStream gradeOutput = new FileOutputStream(grade);
PrintStream gradePrintStream = new PrintStream(gradeOutput)) {
// 写入正确题号
String corrects = String.join(",", correct);
gradePrintStream.println("Correct:" + correct.size() + " (" + corrects + ")");
// 写入错误题号
String wrongs = String.join(",", wrong);
gradePrintStream.println("Wrong:" + wrong.size() + " (" + wrongs + ")");
}
}
}
/**
* 输出题目和答案到指定的PrintStream。
*
* @param index 题目编号
* @param problem 题目和答案数组
* @param streams 可变参数,用于传入输出流
*/
public void outputFile(int index, String[] problem, PrintStream... streams) {
// 确保至少有两个输出流
if (streams.length != 2) {
System.out.println("必须提供两个输出流:一个用于题目,一个用于答案。");
return;
}
try {
// 输出题目到第一个PrintStream
streams[0].println(index + ". " + problem[0]); // 输出题目
// 输出答案到第二个PrintStream
streams[1].println(index + ". " + problem[1]); // 输出答案
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("数组越界。"); // 数组越界异常处理
}
}
}
- MathUtil类
点击查看代码
public class MathUtil {
/**
* 用辗转相除法求最大公因数
* @param x 第一个数
* @param y 第二个数
* @return 最大公因数
*/
public int greatFactor_GCD(int x, int y) {
if (x == 0||y == 0)
return -1;//返回-1,让生成重新生成
while (y != 0) {
int temp = y;
y = x % y;
x = temp;
}
return x; // 当y为0时,x就是最大公因数
}
/**
* 生成一对互质数
* @param range 随机数范围
* @param random 随机数生成器
* @return 互质数数组
*/
public int[] createCoprimeNumbers(int range, Random random) {
int x, y;
// 循环直到找到一对互质数
do {
x = 1 + random.nextInt(range); // 生成随机数 x
y = 1 + random.nextInt(range); // 生成随机数 y
} while (greatFactor_GCD(x, y) != 1); // 检查 x 和 y 是否互质
return new int[]{x, y}; // 返回互质数对
}
/**
* 将假分数转换为真分数
* @param x 分子
* @param y 分母
* @return 真分数字符串
*/
public String shamToProperFraction(int x, int y) {
// 判断是否为假分数
if (x > y) {
int n = x / y; // 计算整数部分
x = x % y; // 计算新的分子
// 如果分子为0,返回整数部分
if (x == 0) {
return String.valueOf(n);
}
// 返回带分数形式,例如 "1'2/3"
return n + "'" + x + "/" + y;
}
// 如果分子和分母相等,返回1
else if (x == y) {
return "1";
}
// 如果分母为1,返回分子
else if (y == 1) {
return String.valueOf(x);
}
// 如果分子为0,返回0
else if (x == 0) {
return "0";
}
// 返回正常分数形式,例如 "2/3"
return x + "/" + y;
}
}
5性能分析
本次的性能分析以生成10000道且操作数范围为10以内的四则运算题目为例
5.1生成的题目与答案
5.2内存消耗
总内存消耗为 304,601 kB,内存方面消耗较高。内存占用主要集中在 String、StringBuilder 和 HashMap,后续可以通过优化这些部分代码来降低应用的内存消耗。
5.3CPU负载
CPU负载最高只在5%左右
5.4性能优化分析
- 字符串使用:
由于 String 和 StringBuilder 的占用较高,代码中有不必要的字符串拼接或频繁创建对象的地方。可以用StringBuilder 来代替字符串拼接。
- HashMap 使用:
HashMap 实例使用过多,可以尝试利用其他数据结构,或是合理调整 HashMap 的初始容量以及负载因子,以减少内存占用。
- 对象复用:
通过对象池或复用模式来降低对象的创建和销毁所带来的内存占用。减少不必要的对象实例:另外减少不必要的临时对象创建,如使用局部变量而不是全局变量。
6单元测试
覆盖率:
测试结果:
测试代码链接:
-测试代码-
7项目小结
在这次编程作业中,我和柯锦宏同学一起讨论,他主要负责编写代码,后期的测试和报告则由我负责,在这一次的作业中,我也从伙伴身上学习到了很多,认识到自己与他人之间的差距,他在编程过程中也耐心的解答我的疑惑,在这个过程中不断的完善代码。两个人一起处理问题,互相汲取对方好的想法,有些细节没有考虑到的,另一个人可以帮忙补充,这样使得效率也大大提高。例如在我们进行最后测试的过程中,我发现当计算答案时如果答案为空则会出错,在这个情况下我们进行讨论之后也克服了这个问题。