软件开发与创新——结对编程

本次结对编程作业由我2252301和学号2252406的同学一起编写

一、题目:小学老师要求出300道四则运算练习题
要求:两个运算符,都是100以内的数字,答案在0-1000之间。
拓展功能:有除法时计算的结果保留两位小数,避免除0错误,回答错误的题目存到“错题本.txt”文件中,方便后续查看及改正。300道题全部答完之后会出现正确题目占比(为了能够及时的看出结果这里以3道题为例)
二、最初版
我们根据题目先简单的写了一个框架代码,之后在此基础上进行优化修改:

点击查看代码
#include<iostream>
#include<cstdlib>
#include<iomanip>
using namespace std;
#define N 300
int  create(){
	int s;
	int p=rand()%4;
	int q=rand()%4;
	int a=rand()%100+1;
	int b=rand()%100+1;
	int c=rand()%100+1;
	cout<<"("<<a;
	switch(p){
	case 0:cout<<"+";
		s=a+b;
		break;
	case 1:cout<<"-";
		s=a-b;
		break;
	case 2:cout<<"*";
		s=a*b;
		break;
	case 3:cout<<"/";
		s=a*1.00/b;
		break;
		}
	cout<<b<<")";
	switch(q){
	case 0:cout<<"+";
		s+=c;
		break;
	case 1:cout<<"-";
		s-=c;
		break;
	case 2:cout<<"*";
		s*=c;
		break;
	case 3:cout<<"/";
		s=s*1.00/c;
		break;
		}
	cout<<c;
	cout<<"=";
	return s;
}
int main(){
	cout<<"以下是随机生成的算术题:"<<endl;
	for(int i=0;i<N;i++){
	int ans,s;
	s=create();
	cin>>ans;
	if(ans==s){
		cout<<"答案正确"<<endl; 
	}
	else
		cout<<"答案错误"<<s<<endl; 
}
	return 0;
}

出现的问题:如果要实现结果在0-1000中,会出现不必要的打印,所以我们将原来的代码打散规整为几个关键的函数,打印的方法也进行了修改。

三、思路及部分代码
1.首先我们要写出随机生成运算符、两个在0-100之间的数字的函数

点击查看代码
// 生成随机整数
int RandomNumber(int min, int max) {
    return rand() % (max - min + 1) + min;
}

// 生成随机运算符
char RandomOperator() {
    char operators[] = { '+', '-', '*', '/' };
    return operators[rand() % 4];
}

2.之后我们考虑到得数需要保留两位小数,我们最开始是搜索有关保留两位小数的方法 但是关于C++基本上都是输出为两位小数,不符合本题的使用;然后我们又开始考虑通过乘以1.00保留两位的小数来做

mid=a*1.00/b
这种形式来得到保留两位小数,多次尝试后得不到想要的结果,后来经过网上查阅发现用round()函数可以实现。
image

double reanswer = round(answer * 100) / 100;

3.关于运算规则我们一开始想的比较简单,就是在前面的加小括号,这样就避免出现歧义。例如:(2-1)*3,这样就避免用户先算乘法而出现错误;
一开始对于乘除优先我们准备放弃,但后来考虑到符合实际的问题,所以我们就开始尝试让乘除先于加减运行,一开始我们是将进行四则运算的函数进行修改,但结果不是很理想,输出的运算符和实际在程序的不一致;后来引入两个个全局变量,进行运算优先级比较,但是效果也是很不理想,导致正确答案都出现了错误!如下图错误:
image

最后我们采用新建的一个优先级函数解决这个问题

点击查看代码
//定义运算符优先级
int precedence(char op) {
    if (op == '+' || op == '-')
        return 1;
    if (op == '*' || op == '/')
        return 2;
    return 0;
}

4.接下来的步骤就比较轻松,首先添加了一个执行四则运算的函数:

点击查看代码
// 执行四则运算
double performOperation(double num1, char op, double num2) {
    switch (op) {
    case '+':
        return num1 + num2;
    case '-':
        return num1 - num2;
    case '*':
        return num1 * num2;
    case '/':
        return num1 / num2;
    default:
        return 0.0;
    }
}
5.在主函数里面编写了避免除0的错误还有如果得数不在0-1000范围内的话要重新出题
点击查看代码
if ((op1 == '/' && num2 == 0) || (op2 == '/' && num3 == 0)) {
    i--;
    continue; // 避免除零错误
}
else {
    if (precedence(op2) > precedence(op1)) {
        intermediateResult = performOperation(num2, op2, num3);
        answer = performOperation(num1, op1, intermediateResult);
    }
    else {
        intermediateResult = performOperation(num1, op1, num2);
        answer = performOperation(intermediateResult, op2, num3);
    }
}
if (answer < 0 || answer>1000)
{
    i--;
    continue;
}
6.最后再来编写导入txt文件的代码

ofstream outfile("错题本.txt");
outfile << "第" << (i + 1) << "题" << ": " << question << " = " << endl << "错误答案:"

7.因为有了错题本的实现,我们又开始考虑是否可以通过读取错题本文件实现错题重做功能,通过回忆之前的学习以及查阅以前写过的代码发现可以辅助实现该功能的某些函数,经过优化整合写出以下代码:

点击查看代码
// 统计 TXT 文件行数的函数
int countLinesInTxt(const string& filename) {
	int lineCount = 0;
	string line;
	ifstream infile(filename); // 打开文件
	if (!infile) {
		cout << "无法打开文件:" << filename << endl;
		return -1; // 返回 -1 表示打开文件失败
	}

	while (getline(infile, line)) {
		lineCount++; // 每读取一行,行数加一
	}

	infile.close(); // 关闭文件
	return lineCount;
}

// 打印指定行的函数
void printSpecificLineFromTxt(const string& filename, int lineToPrint) {
	string line;
	int currentLine = 1;
	ifstream infile(filename); // 打开文件
	if (!infile) {
		cout << "无法打开文件:" << filename << endl;
		return;
	}

	while (getline(infile, line)) {
		if (currentLine == lineToPrint) {
			cout << line;
			return; // 找到指定行并输出后立即返回
		}
		currentLine++;
	}

	// 如果文件中未找到指定行,则输出提示信息
	cout << "文件 " << filename << " 中不存在第 " << lineToPrint << " 行。" << endl;
}

// 删除指定行的函数
void deleteLineFromTxt(const string& filename, int lineToDelete) {
	vector<string> lines; // 存储文件所有行的容器
	string line;
	ifstream infile(filename); // 打开文件
	if (!infile) {
		cout << "无法打开文件:" << filename << endl;
		return;
	}
	while (getline(infile, line)) {
		lines.push_back(line); // 将每行数据存入容器
	}
	infile.close(); // 关闭文件

	if (lineToDelete < 1 || lineToDelete > lines.size()) {
		cout << "要删除的行号超出范围!" << endl;
		return;
	}

	lines.erase(lines.begin() + lineToDelete - 1); // 删除指定行

	ofstream outfile(filename); // 重新打开文件,覆盖原文件内容
	for (const auto& l : lines) {
		outfile << l << endl; // 将剩余行写回文件
	}
	outfile.close(); // 关闭文件
}

以下是实现错题重做的主要代码:

点击查看代码
void review() {
	int flag = countLinesInTxt("错题本.txt");
	int count = 0;
	while (flag) {
		ifstream infile("错题本.txt");
		if (!infile) {
			cout << "无法打开错题文件!" << endl;
			break;
		}
		if (countLinesInTxt("错题本.txt") == 0) {
			cout << "暂无错题!" << endl;
			break;
		}
		else {
			int num1, num2, num3;
			char op1, op2, eq;
			double intermediateResult;
			double answer=0;
			int i = 0;
			
			while (infile >> num1 >> op1 >> num2 >> op2 >> num3 >> eq) {

				if (precedence(op2) > precedence(op1)) {
					intermediateResult = performOperation(num2, op2, num3);
					answer = performOperation(num1, op1, intermediateResult);
				}
				else {
					intermediateResult = performOperation(num1, op1, num2);
					answer = performOperation(intermediateResult, op2, num3);
				}
				double reanswer = round(answer * 100) / 100;//保留两位小数
				double userAnswer;
				// 输出题目并要求用户输入答案
				cout << "第 " << ++count << " 道错题:";
				printSpecificLineFromTxt("错题本.txt", i + 1);
				cin >> userAnswer;
				// 判断答案是否正确
				if (userAnswer == reanswer) {
					cout << "回答正确!" << endl;
					deleteLineFromTxt("错题本.txt", i + 1);
				}
				else {
					cout << "回答错误,正确答案为: " << reanswer << endl;
					i++;
				}
			}
		}
		infile.close();
	}

}

8.之后再进行代码位置的修改以及一些个性化的调整。我们在第二步、第三步、尤其是第七步所耗时间最多,主要是因为忘记了有些知识以及方法的多样性,需要不断进行优化。

四、全部代码及截图
1.第二版代码(未加重做错题版)

点击查看代码
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <string>
#include <cmath>
#include<fstream>

using namespace std;

// 生成随机整数
int RandomNumber(int min, int max) {
    return rand() % (max - min + 1) + min;
}

// 生成随机运算符
char RandomOperator() {
    char operators[] = { '+', '-', '*', '/' };
    return operators[rand() % 4];
}

//定义运算符优先级
int precedence(char op) {
    if (op == '+' || op == '-')
        return 1;
    if (op == '*' || op == '/')
        return 2;
    return 0;
}

// 执行四则运算
double performOperation(double num1, char op, double num2) {
    switch (op) {
    case '+':
        return num1 + num2;
    case '-':
        return num1 - num2;
    case '*':
        return num1 * num2;
    case '/':
        return num1 / num2;
    default:
        return 0.0;
    }
}


int main() {
    srand(time(0)); // 设置随机种子
    ofstream outfile("错题本.txt");
    const int numQuestions = 3; // 生成题目数量
    int correctCount = 0; // 记录答对数量
    cout << "加减乘除的四则运算";
    cout << "(所有结果均保留两位小数!)" << endl;
    // 生成并输出题目
    for (int i = 0; i < numQuestions; ++i) {
        int num1 = RandomNumber(1, 100);
        int num2 = RandomNumber(1, 100);
        int num3 = RandomNumber(1, 100);
        char op1 = RandomOperator();
        char op2 = RandomOperator();

        // 将运算符和数字转换为字符串
        string question = to_string(num1) + " " + op1 + " " + to_string(num2) + " " + op2 + " " + to_string(num3);

        // 计算答案
        double intermediateResult;
        double answer;
        if ((op1 == '/' && num2 == 0) || (op2 == '/' && num3 == 0)) {
            i--;
            continue; // 避免除零错误
        }
        else {
            if (precedence(op2) > precedence(op1)) {
                intermediateResult = performOperation(num2, op2, num3);
                answer = performOperation(num1, op1, intermediateResult);
            }
            else {
                intermediateResult = performOperation(num1, op1, num2);
                answer = performOperation(intermediateResult, op2, num3);
            }
        }
        if (answer < 0 || answer>1000)
        {
            i--;
            continue;
        }
        double reanswer = round(answer * 100) / 100;
        // 输出题目并要求用户输入答案
        cout << "--------------------------------------" << endl;
        cout << "第" << (i + 1) << "题" << ": " << question << " =  ";
        double userAnswer;
        cin >> userAnswer;

        // 判断答案是否正确
        if (userAnswer == reanswer) {
            cout << "回答正确!" << endl;
            correctCount++;
        }
        else {
            cout << "回答错误!" << endl;
            cout<<"正确答案为: " << reanswer << endl;
            outfile << "第" << (i + 1) << "题" << ": " << question << " =  " << endl << "错误答案:" << userAnswer << "   " << "正确答案:" << reanswer << endl;

        }
    }
    outfile.close();
    // 输出总体成绩
    cout << "-------------------------------" << endl;
    cout << "回答正确题目占比: " << correctCount << "/" << numQuestions << endl;
    cout << "注:错题已保留在“错题本.txt”中" << endl;
    cout << "-------------------------------";

    return 0;
}

2.最终版

点击查看代码
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <string>
#include <cmath>
#include<fstream>
#include <vector>

using namespace std;

// 生成随机整数
int RandomNumber(int min, int max) {
	return rand() % (max - min + 1) + min;
}

// 生成随机运算符
char RandomOperator() {
	char operators[] = { '+', '-', '*', '/' };
	return operators[rand() % 4];
}

//定义运算符优先级
int precedence(char op) {
	if (op == '+' || op == '-')
		return 1;
	if (op == '*' || op == '/')
		return 2;
	return 0;
}

// 执行四则运算
double performOperation(double num1, char op, double num2) {
	switch (op) {
	case '+':
		return num1 + num2;
	case '-':
		return num1 - num2;
	case '*':
		return num1 * num2;
	case '/':
		return num1 / num2;
	default:
		return 0.0;
	}
}

// 统计 TXT 文件行数的函数
int countLinesInTxt(const string& filename) {
	int lineCount = 0;
	string line;
	ifstream infile(filename); // 打开文件
	if (!infile) {
		cout << "无法打开文件:" << filename << endl;
		return -1; // 返回 -1 表示打开文件失败
	}

	while (getline(infile, line)) {
		lineCount++; // 每读取一行,行数加一
	}

	infile.close(); // 关闭文件
	return lineCount;
}

// 打印指定行的函数
void printSpecificLineFromTxt(const string& filename, int lineToPrint) {
	string line;
	int currentLine = 1;
	ifstream infile(filename); // 打开文件
	if (!infile) {
		cout << "无法打开文件:" << filename << endl;
		return;
	}

	while (getline(infile, line)) {
		if (currentLine == lineToPrint) {
			cout << line;
			return; // 找到指定行并输出后立即返回
		}
		currentLine++;
	}

	// 如果文件中未找到指定行,则输出提示信息
	cout << "文件 " << filename << " 中不存在第 " << lineToPrint << " 行。" << endl;
}

// 删除指定行的函数
void deleteLineFromTxt(const string& filename, int lineToDelete) {
	vector<string> lines; // 存储文件所有行的容器
	string line;
	ifstream infile(filename); // 打开文件
	if (!infile) {
		cout << "无法打开文件:" << filename << endl;
		return;
	}
	while (getline(infile, line)) {
		lines.push_back(line); // 将每行数据存入容器
	}
	infile.close(); // 关闭文件

	if (lineToDelete < 1 || lineToDelete > lines.size()) {
		cout << "要删除的行号超出范围!" << endl;
		return;
	}

	lines.erase(lines.begin() + lineToDelete - 1); // 删除指定行

	ofstream outfile(filename); // 重新打开文件,覆盖原文件内容
	for (const auto& l : lines) {
		outfile << l << endl; // 将剩余行写回文件
	}
	outfile.close(); // 关闭文件
}

void review() {
	int flag = countLinesInTxt("错题本.txt");
	int count = 0;
	while (flag) {
		ifstream infile("错题本.txt");
		if (!infile) {
			cout << "无法打开错题文件!" << endl;
			break;
		}
		if (countLinesInTxt("错题本.txt") == 0) {
			cout << "暂无错题!" << endl;
			break;
		}
		else {
			int num1, num2, num3;
			char op1, op2, eq;
			double intermediateResult;
			double answer=0;
			int i = 0;
			
			while (infile >> num1 >> op1 >> num2 >> op2 >> num3 >> eq) {

				if (precedence(op2) > precedence(op1)) {
					intermediateResult = performOperation(num2, op2, num3);
					answer = performOperation(num1, op1, intermediateResult);
				}
				else {
					intermediateResult = performOperation(num1, op1, num2);
					answer = performOperation(intermediateResult, op2, num3);
				}
				double reanswer = round(answer * 100) / 100;//保留两位小数
				double userAnswer;
				// 输出题目并要求用户输入答案
				cout << "第 " << ++count << " 道错题:";
				printSpecificLineFromTxt("错题本.txt", i + 1);
				cin >> userAnswer;
				// 判断答案是否正确
				if (userAnswer == reanswer) {
					cout << "回答正确!" << endl;
					deleteLineFromTxt("错题本.txt", i + 1);
				}
				else {
					cout << "回答错误,正确答案为: " << reanswer << endl;
					i++;
				}
			}
		}
		infile.close();
	}

}

int main() {
	srand(time(0)); // 设置随机种子
	ofstream outfile("错题本.txt");
	const int numQuestions = 3; // 生成题目数量
	int correctCount = 0; // 记录答对数量
	cout << "加减乘除的四则运算";
	cout << "(所有结果均保留两位小数!)" << endl;
	// 生成并输出题目
	for (int i = 0; i < numQuestions; ++i) {
		int num1 = RandomNumber(1, 100);
		int num2 = RandomNumber(1, 100);
		int num3 = RandomNumber(1, 100);
		char op1 = RandomOperator();
		char op2 = RandomOperator();

		// 将运算符和数字转换为字符串
		string question = to_string(num1) + " " + op1 + " " + to_string(num2) + " " + op2 + " " + to_string(num3);

		// 计算答案
		double intermediateResult;
		double answer;
		if ((op1 == '/' && num2 == 0) || (op2 == '/' && num3 == 0)) {
			i--;
			continue; // 避免除零错误
		}
		else {
			if (precedence(op2) > precedence(op1)) {
				intermediateResult = performOperation(num2, op2, num3);
				answer = performOperation(num1, op1, intermediateResult);
			}
			else {
				intermediateResult = performOperation(num1, op1, num2);
				answer = performOperation(intermediateResult, op2, num3);
			}
		}
		if (answer < 0 || answer>1000) {
			i--;
			continue;
		}
		double reanswer = round(answer * 100) / 100;
		// 输出题目并要求用户输入答案
		cout << "--------------------------------------" << endl;
		cout << "第" << (i + 1) << "题" << ": " << question << " =  ";
		double userAnswer;
		cin >> userAnswer;

		// 判断答案是否正确
		if (userAnswer == reanswer) {
			cout << "回答正确!" << endl;
			correctCount++;
		}
		else {
			cout << "回答错误,正确答案为: " << reanswer << endl;
			outfile << question << " =  " << endl;

		}
	}
	outfile.close();
	// 输出总体成绩
	cout << "-------------------------------" << endl;
	cout << "回答正确题目占比: " << correctCount << "/" << numQuestions << endl;
	cout << "注:错题已保留在“错题本.txt”中" << endl;
	cout << "-------------------------------"<<endl;
	cout << "是否选择回做错题" << "(1.是 2.否):";
	int choice;
	cin >> choice;
	if (choice == 1) {
		review();
	}
	return 0;
}

image

image

五、总结与反思
1.反思:因为时间问题我们仍有一些未优化的功能,代码的复用性不高,结构性较散乱,例如,如果能够实现清屏功能,就能使重做错题的效果更加好。对于某些函数熟悉度不够高,从而导致写代码的速度降低甚至没有想法。有些功能的实现方法有很多,但是很多时候想法过于局限导致实现方法繁琐,没有条理。

2.总结
2252301:通过本次结对编程我们体会到看似简单的题目其实有很多细节问题需要注意,因为是两个人一起写一个项目,所以这里就明显的体现出注释的重要性。并且在编程过程中可以有效提高代码质量,减少错误的产生。通过实时交流,问题和解决方案可以迅速被讨论和实施。
2252406:在面对同一个问题时由于两个人的想法不一致有很多种方法去解答,我们共同寻找最简单明了的方法。同时也通过老师提示一步一步去完成题目中的要求并且也拓展了一些内容,使页面具有个性化。在结对过程中,能力较强的同学可以将知识传授给能力较弱的同学;还可以促进团队合作,增强小组成员的适应性和灵活性。当出现问题时,结对编程的伙伴可以共同承担责任,减少个人压力。

posted on 2024-04-23 16:20  拉绒  阅读(44)  评论(0)    收藏  举报