2023软件工程第二次作业(结对编程)

姓名:李英磊    学号:M23182606

姓名:邵卓承    学号:M23182705

       按课程要求完成结队编程四则运算自动生成题目与答案并且自动判卷的项目。

       我们的项目仓库代码如下:李英磊/邵卓承结对编程代码仓库

       整个项目工程中做的具体分工如下:

       李英磊:创建仓库、需求分析、完成代码、仓库管理、协助完成博客

       邵卓承:学习代码、代码分析、根据需求改进优化代码、撰写博客

一、PSP个人开发流程表格

PSPPersonal Software Process Stages预估耗时实际耗时
Planning计划1010
Estimate估计这个任务需要多少时间1010
Development开发200150
Analysis需求分析50100
Coding代码规范2025
Code Review代码复审2020
Test测试1010
Reporting报告3030
Test Repor测试报告1010
Size Measurement计算工作量1010
Postmortem & Process Lmprovement plan事后总结,并提出过程改进计划2020
合计390395

二、 工程进行中学习日志以及对题目的思考

(一)学习过程

邵卓承:

10.16——学习使用可视化工具TortoiseGit上传与下载码云仓库代码(时间1、2、9、10节课)

       学习进度:已基本学会使用,并且通过实践。

       评价:但是由于缺乏常识,学习效率低。

10.17—— 通过bilbili网课学习码云仓库的里程碑、任务发布以及commit等操作(时间1,2节课)

       学习进度:完成学习,并完成相关任务。

       评价:任务简单,完成容易。

10.18——尝试读懂下载的代码,并考虑八条需求中哪些可以简单通过改动完成(时间3、4节课)

       学习进度:通过各种手段基本弄清每个函数的功能,并且能过通过改动一些值改变代码部分功能

       评价:并未完全吃透代码,只是以使用者的角度对代码进行理解 

10.19——更改和调整部分代码,学习分析VS的效能分析图(时间1、2、3、4节课)

       学习进度:对代码进行了微调,效能分析的学习过程中了解到大量分析软件,VS效能分析图能够初步理解意义

       评价:能基本完成任务需求,但是不够精通

10.20——写博客(时间1,2节课)

       学习进度:基本完成整体的博客撰写

       评价:较高效

10.21——完善博客(时间1、2节课)

       学习进度:完成两人沟通与合作,完成博客撰写

       评价:较高效

李英磊:

10.16——创建自己的仓库,在互联网查资料并寻找合适的代码(时间1、2节课)

       学习进度:查阅资料,加强知识巩固

       评价:高效

10.17——读懂代码,将下载的代码进行改进,并考虑需求中简单的一部分(时间3、4节课)

       学习进度:对代码进行基本的改进并能完成一些简单需求

       评价:高效

10.18——将下载的代码进一步改进,并考虑加入更多需求(时间1、2节课)

       学习进度:进一步改进代码并能完成更多需求

       评价:高效

10.19——对代码进行效能分析并编写PSP表格(时间3、4节课)

       学习进度:查找资料对代码进行效能分析完成对PSP表格的编写

       评价:高效

10.20——写博客(时间9、10节课)

       学习进度:对博客的内容进行编写

       评价:高效

10.21——完善博客(时间1、2节课)

       学习进度:进一步完善对博客内容的编写

       评价:高效

(二)对题目的思考 

        根据代码的八条需求:

  1. 题目数量可以指定。
  2. 支持加减乘除4种运算。
  3. 每道题包含两个运算符
  4. 运算数为100 以内的数字。
  5. 保证答案在 0..100 之间。
  6. 需要有答题功能并验证答案是否正确。
  7. 判分,并对历史成绩进行存储和查询。
  8. 题目避免重复。

        除此之外还有一条附加需求:题目含有可嵌套括号,以改变运算优先级

        根据以上需求,以及代码出题、出答案、给出答题结果的功能将代码算法设计如下:

       根据需求中的几项,无疑生成题目阶段需要实现的功能很多,比如判断分母不能为0,出的题目不能重复,随机插入前括号,然后根据前括号位置插入后括号,以及题目中的运算数的类型以及大小,这些都需要在出题阶段实现限制与操作。其中读取文件、写入文件等也是难点之一。

       根据这些需求,由于编程基础较差,对于文件相关编程的操作不熟悉,对随机数以及字符串的操作也不熟练,所以考虑在网上拷贝功能相似的代码,并且理解代码,并在其基础上修改以达到代码的功能以及需求。

三、代码设计与解读

(一)创建文档以及代码与文档建立联系

#define EXERCISEFILE	"exercisefile.txt"
#define ANSWERFILE		"answerfile.txt"
#define GRADE			"grade.txt"

(二) 初始化文档

remove(EXERCISEFILE);
remove(ANSWERFILE);

(三)输入操作代码以及参数

默认设置出题数值范围为100:

int size = 100;	//计算数的取值范围

输入-n设置出题数量:

case 'n'://控制生成题目的个数
	n = 2;
	while (instructions[n] == ' ' && n < 300) n++;
	if (n == 2 || n == 300)
	{
		cout << "指令输入有误,请重新输入!\n";
		break;
	}
	i = 10;
	amount = 0;
	while (instructions[n] != '\0' && instructions[n] >= '0' && instructions[n] <= '99' && n < 300)
	{
		j = instructions[n] - '0';
		amount = j + amount * i;
		n++;
	}
	if (amount < 1 && n == 300 && amount > MAX_LENGTH)
	{
		cout << "数据输入有误,请重新输入!\n";
		break;
	}
	remove(EXERCISEFILE);
	remove(ANSWERFILE);
	M_instructions(expression, amount, size, result);
	Destrory(expression, result, amount);
	break;

 输入-r设置每个数字的大小范围

case 'r'://控制题目中数值(自然数、真分数和真分数分母)的范围
	n = 2;
	while (instructions[n] == ' ' && n < 300) n++;
	size = 0;
	if (n == 2 || n == 300)
	{
		cout << "指令输入有误,请重新输入!\n";
		break;
	}
	while (instructions[n] != '\0' && instructions[n] >= '0' && instructions[n] <= '99' && n < 300)
	{
		size = instructions[n] - '0' + size * 10;
		n++;
	}
	if (size <= 0 || n == 300)
	{
		size = 100;
		cout << "数据输入有误,请重新输入!\n";
	}
	break;

(四)生成题目与答案

        生成题目、答案、写入文档操作的代码如下:

        随机生成随机题目表达式的代码如下:

Status Create(var** exp, int size, int* length)
{
	var* expre;
	int mark_num = 2;//计算符个数
	int pre = 0;//前括号在第pre个数字前
	int aft = 0;//后括号在第aft个数字后
	int judge = 0;//判断,0写入数字,1写入符号
	int n = 0;
	*length = mark_num + mark_num + 1;
	n = 0;
	//如果运算符有3个,则存在括号
	if (mark_num > 1)
	{
		pre = random(1, 3);
		if (pre == 1)//不让括号括住整个式zi
			aft = random((pre + 1), (mark_num + 1));
		else
			aft = random((pre + 1), (mark_num + 2));
		(*length) += 2;
		expre = new var[*length + 1];
		expre[pre * 2 - 2].num_or_Symbol = 1;
		expre[pre * 2 - 2].Symbol = 4;
		expre[aft * 2].num_or_Symbol = 1;
		expre[aft * 2].Symbol = 5;
	}
	else
	{
		expre = new var[*length + 1];
	}
	n = 0;
	while (n < *length)
	{
		if (expre[n].Symbol < 4)
		{
			if (judge == 0)
			{
				expre[n].num_or_Symbol = 0;
				expre[n].Den = random(2, size);
				expre[n].numer = random(0, expre[n].Den);
				expre[n].num = random(1, size);
				judge = 1;
			}
			else
			{
				expre[n].num_or_Symbol = 1;
				expre[n].Symbol = random(0, 4);
				judge = 0;
			}
		}
		n++;
	}
	*exp = expre;
	return SUCCESS;
}

void Visit(var* expression, int length, int num)
{
	fstream exercise;
	exercise.open(EXERCISEFILE, ios::out | ios::app);
	exercise << num << ".   ";
	int n = 0;
	while (n < length)
	{
		if (expression[n].num_or_Symbol == 0)
		{
			if (expression[n].numer == 0)
			{
				cout << expression[n].num;
				exercise << expression[n].num;
			}
			else
			{
				if (expression[n].num != 0)
				{
					cout << expression[n].num;
					cout << "`";
					exercise << expression[n].num;
					exercise << "`";
				}
				cout << expression[n].numer;
				cout << "/";
				cout << expression[n].Den;
				exercise << expression[n].numer;
				exercise << "/";
				exercise << expression[n].Den;
			}
		}
		else
		{
			switch (expression[n].Symbol)
			{
			case 0:
				cout << " + ";
				exercise << " + ";
				break;
			case 1:
				cout << " - ";
				exercise << " - ";
				break;
			case 2:
				cout << " × ";
				exercise << " × ";
				break;
			case 3:
				cout << " ÷ ";
				exercise << " ÷ ";
				break;
			case 4:
				cout << '(';
				exercise << '(';
				break;
			case 5:
				cout << ')';
				exercise << ')';
				break;
			default:break;
			}
		}
		n++;
	}

	cout << " = \n";
	exercise << " = ";
	exercise << endl;
	exercise.close();
}

       其中,控制括号的代码行 

case 4:
	cout << '(';
	exercise << '(';
	break;
case 5:
	cout << ')';
	exercise << ')';
	break;

       由代码可见,该代码生成的随机数为分数,符号包含了四则运算,且避免了分母为0的情况。

       通过答案是否相等来初步判断题目是否重复的功能,代码如下:

void M_instructions(var** expression, int amount, int size, var* result)
{
	fstream answer;
	answer.open(ANSWERFILE, ios::out | ios::app);
	var results[maxn];//后缀表达式
	int length;
	int i = 0;
	int j = 0;
	int k = 0;
	while (i < amount)
	{
		Create(&expression[i], size, &length);
		result[i].Symbol = length;
		if (Calculation(expression[i], size, results, length) == ERROR || results[0].num >= size || results[0].numer >= size || results[0].Den >= size)
		{
			continue;
		}
		result[i].Den = results[0].Den;
		result[i].num = results[0].num;
		result[i].numer = results[0].numer;
		result[i].num_or_Symbol = 0;
		result[i].Symbol = length;
		j = 0;
		while (j < i)
		{
			//结果一样,表达式可能一样
			if (result[j].Den == result[i].Den && result[j].numer == result[i].numer && result[j].num == result[i].num)
			{
				if (is_question_same(expression[i], result[i].Symbol, expression[j], result[j].Symbol, size))
				{
					break;
				}
			}
			j++;
		}
		if (i != j)
		{
			if (k++ < 20)//连续20次重复答案表明给的size太小,而amount太大,表达式多样性不足
				continue;
		}
		Visit(expression[i], length, i + 1);

		answer << i + 1 << ".   ";
		if (result[i].numer == 0)
		{
			answer << result[i].num;
		}
		else
		{
			if (result[i].num != 0)
			{
				answer << result[i].num;
				answer << "`";
			}
			answer << result[i].numer;
			answer << "/";
			answer << result[i].Den;
		}
		answer << endl;
		i++;
		k = 0;
	}
	answer.close();
}

        根据文本内容判断是否答题正确的代码如下:

void Correction(int* save, char* answerfile, char* exercisefile)// -e exercisefile.txt -a answerfile.txt
{
	fstream answer;
	fstream exercise;
	fstream grade;
	answer.open(answerfile, ios::in);
	if (!answer)
	{
		cout << "答案文件不存在!\n";
		return;
	}
	exercise.open(exercisefile, ios::in);
	if (!exercise)
	{
		cout << "题目文件不存在!\n";
		return;
	}
	grade.open(GRADE, ios::out | ios::app);
	char answ[100];
	char exer[100];
	int i, j;
	int n, m;
	int correct = 0;
	int wrong = 0;
	while (!answer.eof() && !exercise.eof())
	{
		answer.getline(answ, 100);//读取整行
		exercise.getline(exer, 100);
		i = 0, j = 0, n = 0, m = 0;
		while (answ[i] >= '0' && answ[i] <= '9')
			n = answ[i++] - '0' + n * 10;
		while (exer[j] >= '0' && exer[j] <= '9')
			m = exer[j++] - '0' + m * 10;
		if (n == m)
		{
			if (n != 0 && n <= MAX_LENGTH)
			{
				i = 0;
				j = 0;
				while (exer[j++] != '=' && j < 100);
				while (exer[j++] == ' ' && j < 100);
				while (answ[i++] != '.' && i < 100);
				while (answ[i++] == ' ' && i < 100);
				j -= 1;
				i -= 1;
				while (exer[j] != ' ' && exer[j] != '\t' && exer[j] != '\0' && i < 100 && j < 100)
				{
					if (answ[i++] != exer[j++])
						break;
				}
				if (answ[i - 1] == exer[j - 1] && exer[j - 1] != ' ' && answ[i] == '\0')
				{
					save[n] = 1;
					save[n + 1] = -1;
					correct++;
				}
				else
				{
					save[n] = 0;
					save[n + 1] = -1;
					wrong++;
				}
			}
		}
		else
		{
			if (n == 0 && m != 0)
				n = m;
			if (m == 0 && n != 0)
				m = n;
			cout << "操作失败!文件" << ANSWERFILE << "第" << n << "题或" << EXERCISEFILE << "第" << m << "题发生错误!\n";
			return;
		}
	}
	i = 1, j = 0;
	grade << "Correct: " << correct << " (";
	while (save[i] >= 0)
	{
		if (save[i] == 1)
		{
			if (j != 0)
				grade << ',';
			else
				j = 1;
			grade << i;
		}
		i++;
	}
	grade << ")" << endl;
	i = 1, j = 0;
	grade << "Wrong: " << wrong << " (";
	while (save[i] >= 0)
	{
		if (save[i] == 0)
		{
			if (j != 0)
				grade << ',';
			else
				j = 1;
			grade << i;
		}
		i++;
	}
	grade << ")" << endl;
	cout << "数据已录入" << GRADE << "!\n";
	answer.close();
	exercise.close();
	grade.close();
}

void Destrory(var** expression, var* result, int amount)
{
	int i = 0;
	while (i < amount)
		free(expression[i++]);

        随机括号的位置,增加解题的难度,代码如下:

Status Infix_to_Postfix(var* p, int size, var* Postfix, int length, int& postLen)
{
	//传入的postfix要记得为空
	var stack[maxn];
	int top = 0;
	for (int i = 0; i < length; i++)
	{
		if (p[i].num_or_Symbol == 0)//是数字
		{
			Postfix[postLen++] = p[i];//放入输出串中
		}
		if (p[i].num_or_Symbol == 1 && p[i].Symbol == 4)//左括号
		{
			++top;
			stack[top] = p[i];
		}
		while (p[i].num_or_Symbol == 1 && p[i].Symbol != 4 && p[i].Symbol != 5)
		{
			if (top == 0 || stack[top].Symbol == 4 || prio(p[i]) > prio(stack[top]))
			{
				++top;
				stack[top] = p[i];
				break;
			}
			else
			{
				Postfix[postLen++] = stack[top];
				top--;
			}
		}
		if (p[i].num_or_Symbol == 1 && p[i].Symbol == 5)//右括号
		{
			while (stack[top].Symbol != 4)
			{
				Postfix[postLen++] = stack[top];
				top--;
			}
			top--;
		}
	}
	while (top != 0)
	{
		Postfix[postLen++] = stack[top--];
	}
	return SUCCESS;
}

int prio(var element)
{
	if (element.Symbol == 0 || element.Symbol == 1)
		return 1;
	if (element.Symbol == 2 || element.Symbol == 3)
		return 2;
	return 0;
}

        分数的通分运算,生成答案并将答案写入文档的过程,代码如下:

void preprocess(var& a, var& b)
{
	//将前面的系数加进分母
	if (a.num != 0)
	{
		a.numer += a.num * a.Den;
		a.num = 0;
	}
	if (b.num != 0)
	{
		b.numer += b.num * b.Den;
		b.num = 0;
	}
	//分母通分
	a.numer *= b.Den;
	b.numer *= a.Den;
	a.Den = b.Den = a.Den * b.Den;
}

int get_gcd(int a, int b) {
	return b == 0 ? a : get_gcd(b, a % b);
}

Status calc(var* Postfix, int length, var& ans)
{
	var stack[maxn];//存放操作数
	int top = 0;
	for (int i = 0; i < length; i++)//遍历后缀表达式
	{
		if (Postfix[i].num_or_Symbol == 0)//是操作数
		{
			stack[top++] = Postfix[i];
		}
		else if (Postfix[i].num_or_Symbol == 1)//是运算符
		{
			if (top < 2)//判断一下操作数是否足够两个,不足够则报错
			{
				printf("发生错误,操作数不足,无法完成运算\n");
				exit(-1);
			}
			var ret;
			if (!operate(stack[top - 2], stack[top - 1], Postfix[i], ret))//计算过程有负数 || 计算过程中出现了除 0 的非法操作
			{
				return ERROR;
			}
			top -= 2;
			stack[top++] = ret;
		}
	}
	if (top == 1)//最后肯定只剩一个数字
	{
		ans = stack[0];
		return SUCCESS;
	}
	//printf("计算到最后,栈中剩余的操作数不为一个\n");
	return ERROR;
}

Status operate(var a, var b, var oper, var& ret)
{
	//先通分
	preprocess(a, b);
	//判断执行的操作
	if (oper.Symbol == 0)//+
	{
		a.numer += b.numer;
	}
	else if (oper.Symbol == 1)//-
	{
		a.numer -= b.numer;
	}
	else if (oper.Symbol == 2)// *
	{
		a.numer *= b.numer;
		a.Den *= b.Den;
	}
	else if (oper.Symbol == 3)// ÷
	{
		a.numer *= b.Den;
		a.Den *= b.numer;
	}
	if (a.numer < 0 || a.Den == 0)//计算过程中出现了负数 || 计算过程中出现了除 0 的非法操作
	{
		return ERROR;
	}
	//化简
	ret = a;
	int gcd = get_gcd(ret.numer, ret.Den);
	ret.numer /= gcd, ret.Den /= gcd;
	ret.num = ret.numer / ret.Den;
	ret.numer %= ret.Den;
	return SUCCESS;
}

void display(var* a) {
	if (a->num == 0)
	{
		if (a->Den == 1)
		{
			printf("%d\n", a->numer);
		}
		else {
			printf("%d/%d\n", a->numer, a->Den);
		}
	}
	else {
		if (a->Den == 1)
		{
			printf("%d\n", a->numer + a->num);
		}
		else {
			printf("%d'%d/%d\n", a->num, a->numer, a->Den);
		}
	}
}

void crerat_info(var* expression, int i, int num_or_Symbol, int numer, int den, int Symbol, int num)
{
	expression[i].num_or_Symbol = num_or_Symbol;
	expression[i].Den = den;
	expression[i].numer = numer;
	expression[i].Symbol = Symbol;
	expression[i].num = num;
}

void print(var* result, int len)
{
	for (int i = 0; i < len; i++)
	{
		if (result[i].num_or_Symbol == 0)
		{
			printf("%d", result[i].numer);
		}
		else {
			if (result[i].Symbol == 0)
			{
				printf("+");
			}
			else if (result[i].Symbol == 1)
			{
				printf("-");
			}
			else if (result[i].Symbol == 2)
			{
				printf("*");
			}
			else if (result[i].Symbol == 3)
			{
				printf("÷");
			}
		}
	}
	puts("");
}
//计算表达式函数
Status Calculation(var* exp, int size, var* result, int length)
{
	int postLen = 0;
	Infix_to_Postfix(exp, size, result, length, postLen);
	var ans;
	if (!calc(result, postLen, ans))
	{
		return ERROR;
	}
	*result = ans;
	return SUCCESS;
}

Status get_Subexpression(var* Postfix, int length, var ans[][3], int& sta_size)
{
	var stack[maxn];//存放操作数
	int top = 0;
	for (int i = 0; i < length; i++)//遍历后缀表达式
	{
		if (Postfix[i].num_or_Symbol == 0)//是操作数
		{
			stack[top++] = Postfix[i];
		}
		else if (Postfix[i].num_or_Symbol == 1)//是运算符
		{
			var ret;

			ans[sta_size][0] = stack[top - 2];
			ans[sta_size][1] = stack[top - 1];
			ans[sta_size++][2] = Postfix[i];

			operate(stack[top - 2], stack[top - 1], Postfix[i], ret);
			top -= 2;
			stack[top++] = ret;
		}
	}
	return SUCCESS;
}

        判断随机题目是否一样,代码如下:

bool cmp(var a[], var b[])
{
	//判断子表达式是否等价
	if (a[2] == b[2])//运算符
	{
		if (a[0] == b[1] && a[1] == b[0] || (a[0] == b[0] && a[1] == b[1]))//操作数
		{
			return true;
		}
	}
	return false;
}
//判断两个问题是否等价
Status is_question_same(var* Question, int lenQuest, var* newQuestion, int lenNewQuest, int size)
{
	var Postfix1[maxn], Postfix2[maxn];
	var stack1[3][3], stack2[3][3];
	int len1 = 0, len2 = 0, sta_size1 = 0, sta_size2 = 0;

	//获取后缀表达式
	Infix_to_Postfix(Question, size, Postfix1, lenQuest, len1);
	Infix_to_Postfix(newQuestion, size, Postfix2, lenNewQuest, len2);

	//获取子表达式
	get_Subexpression(Postfix1, len1, stack1, sta_size1);
	get_Subexpression(Postfix2, len2, stack2, sta_size2);

	bool flag;
	for (int i = 0; i < sta_size1; i++)
	{
		flag = false;
		for (int j = 0; j < sta_size2; j++)
		{
			//短式等价
			if (cmp(stack1[i], stack2[j]))
			{
				flag = true;
				stack2[j][2].Symbol = -1;//将表达式的运算符删掉
				break;
			}
		}
		if (!flag)//如果存在不一样的,返回not same
		{
			return ERROR;
		}
	}
	return SUCCESS;
}

        生成题目与答案的运行结果如下图所示:

(五)做题

 

        如图,将答案写入题目txt文件,题目正确三题,错误一题,未答六题 。

(六)批改

        对运行输入-e+题目文件名+答案文件名对做题进行批改,并将结果写入文档。

case 'e':
	char exercisefile[100];
	char answerfile[100];
	n = 2;
	while (instructions[n] == ' ' && n < 300) n++;
	if (n == 2)
	{
		cout << "指令输入有误,请重新输入!\n";
		break;
	}
	i = n;
	while (instructions[n] != ' ' && n - i < 100 && n < 300 && instructions[n] != '\0')
	{
		exercisefile[n - i] = instructions[n];
		n++;
	}
	exercisefile[n - i] = '\0';
	while (instructions[n] == ' ' && n < 300) n++;
	if (instructions[n] != '-' || instructions[n + 1] != 'a')
	{
		cout << "指令输入有误,请重新输入!\n";
		break;
	}
	n = n + 2;
	i = n;
	while (instructions[n] == ' ' && n < 300) n++;
	if (i == n)
	{
		cout << "指令输入有误,请重新输入!\n";
		break;
	}
	i = n;
	while (instructions[n] != ' ' && n - i < 100 && n < 300 && instructions[n] != '\0')
	{
		answerfile[n - i] = instructions[n];
		n++;
	}
	answerfile[n - i] = '\0';
	if (n == 300)
	{
		cout << "指令输入过长,请重新输入!\n";
		break;
	}
	Correction(save, answerfile, exercisefile);
	break;
default:
	cout << "指令输入有误,请重新输入!\n";
	break;
}

        如图,对上图的做题结果统计出正确个数与题号和错误个数与题号。

四、代码性能分析以及改进

(一)单元测试

1.输入错误指令

2.输入超范围数值

3.生成式子数量等于零、小于零、大于零和等于10的情况,小于等于零是否按0算

4.设置运算数大小成功后出题,运算数是否对应设置值

5.设置运算数大小失败后出题,运算数是否恢复默认值

6.文本生成是否正确,答案的计算值和手动计算一致

7.批改是否能识别,答题结果是否能存储

  

(二)性能分析

         代码中所有内部函数的运行次数以及运行占比如图所示。

 (三)改进

        由于代码非常复杂,且在编译器中运行没有错误,加之小组内人员水平有限,从代码效能的角度对代码进行改进存在困难,故对代码的功能实现更改以贴合需求。

        基于需求中计算数范围为0-100对代码进行了修改,改动如下。

        默认数据范围更改:

int size = 100;//10;	//计算数的取值范围

cout << "注:运算数大小默认为小于100的正自然数,可使用指令更改为任意大于0小于2147483647的整数!\n";

        设置数值错误后恢复的数值更改: 

if (size <= 0 || n == 300)
{
				size = 100;// 10;
				cout << "数据输入有误,请重新输入!\n";
}

        综合后代码满足了七个需求: 

  1. 题目数量可以指定。
  2. 支持加减乘除4种运算
  3. 每道题包含两个运算符
  4. 运算数为100 以内的数字
  5. 需要有答题功能并验证答案是否正确
  6. 判分,并对历史成绩进行存储和查询
  7. 题目避免重复

五、学习心路历程与收获

(一)工程项目历程

        编辑里程碑并分配任务

        对代码仓库的一些操作,两根共同在一个分支中作业每次编程都重新同步拉取代码,编辑后再上传 。

 

 (二)学习心路历程以及收获

        邵卓承:

        项目开始初期,由于受本课程相关专业同学的启发,对码云的代码克隆、拉取、推送有了一个更深入的认识,并且了解到有小乌龟这样一个可视化工具,帮助代码的管理,由于缺乏计算机相关的一些常识性知识,学习起来历经坎坷,花了四个学时才学会使用,但对后面结对编程有很大帮助,也是一大收获。

       在找代码、读代码阶段,有些语言不太理解,通过在网上搜索、在一些平台看网课、并且询问了一些本专业高中同学帮助理解,学会了一些新功能的库以及语言,但是对于头文件源文件以及读取文本等的依然理解不深,仍然需要时间深入学习。

       初步尝试了利用码云平台,以一个管理者身份对一个工程仓库进行操作,编辑里程碑,拉取合作者,分配任务等操作,对一个项目的创建有了一个初步的认识。

       写代码水平毋庸置疑仍然是迫在眉睫的困境,在课程学习的短短六周内虽然有很大的进步,但仍然需要继续学习。由于对课程理解的偏差,选修此课程的收获有些背离了初衷,但是也极大程度地拓宽了视野,了解了软件工程行业内地基本流程与情况,也让我对编程有了兴趣,对将来我的专业学习有一定帮助。

       这门课程的学习对我来说无疑是一项挑战,但课程具有趣味性,我也愿意花时间和精力来钻研和学习课程的本领,了解课程的历史与现状,经过了五周痛并快乐的学习我感觉受益匪浅,给我开启了一扇崭新的大门。在以后的课程学习过程中以及最后的小组编程中,我仍会保有高涨的热情,也希望能有很多收获。

       李英磊:

       第一次面对有一定难度的项目有一些手足无措,但随着后来学习的深入,发现其实也并没有想象的那么难,加上有同学老师的帮助,完成这个项目变得更简单些,自己从一个对软件工程什么都不懂的小白也逐渐变得入门。

       软件工程课程拓宽了我的视野。在课堂上,我们学习了软件开发的整个过程,包括需求分析、设计、编码、测试和维护等环节。通过学习这些内容,我明白了软件开发不仅仅是编写代码,还需要考虑用户需求、系统设计和质量保证等方面。软件工程课程让我认识到,一个好的软件开发团队应该具备合理的组织架构和良好的团队合作能力,才能开发出高质量的软件。


       软件工程课程提高了我的实践能力。课程中,我们进行了一些实践项目,从需求分析到软件开发,再到测试和维护。通过这些实践项目,我不仅巩固了理论知识,还学会了如何将理论应用到实际项目中。在实践中,我遇到了很多问题,比如需求不明确、设计不合理等,但是通过团队的努力和老师的指导,我逐渐掌握了解决问题的方法和技巧。这些实践项目对我来说是一次宝贵的经验积累,提高了我的实践能力和解决问题的能力。

六、第二次需求增加改动部分

控制括号的代码行 

case 4:
	cout << '(';
	exercise << '(';
	break;
case 5:
	cout << ')';
	exercise << ')';
	break;

  随机括号的位置,增加解题的难度,代码如下:

Status Infix_to_Postfix(var* p, int size, var* Postfix, int length, int& postLen)
{
	//传入的postfix要记得为空
	var stack[maxn];
	int top = 0;
	for (int i = 0; i < length; i++)
	{
		if (p[i].num_or_Symbol == 0)//是数字
		{
			Postfix[postLen++] = p[i];//放入输出串中
		}
		if (p[i].num_or_Symbol == 1 && p[i].Symbol == 4)//左括号
		{
			++top;
			stack[top] = p[i];
		}
		while (p[i].num_or_Symbol == 1 && p[i].Symbol != 4 && p[i].Symbol != 5)
		{
			if (top == 0 || stack[top].Symbol == 4 || prio(p[i]) > prio(stack[top]))
			{
				++top;
				stack[top] = p[i];
				break;
			}
			else
			{
				Postfix[postLen++] = stack[top];
				top--;
			}
		}
		if (p[i].num_or_Symbol == 1 && p[i].Symbol == 5)//右括号
		{
			while (stack[top].Symbol != 4)
			{
				Postfix[postLen++] = stack[top];
				top--;
			}
			top--;
		}
	}
	while (top != 0)
	{
		Postfix[postLen++] = stack[top--];
	}
	return SUCCESS;
}

int prio(var element)
{
	if (element.Symbol == 0 || element.Symbol == 1)
		return 1;
	if (element.Symbol == 2 || element.Symbol == 3)
		return 2;
	return 0;
}

生成题目如图所示 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值