结对项目:用C++实现四则运算

软工作业3:自动生成小学四则运算题目的命令行程序

这个作业属于哪个课程 计科21级1 2班
这个作业要求在哪里 结对项目
这个作业的目标 熟悉合作开发流程
项目Github 点击这里
团队成员
姓名 学号
石云欣 3221004809
沈纪康 3121004750

PSP表

PSP2.1 Personal Software Process 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 20 10
· Estimate · 估计这个任务需要多少时间 20 10
Development 开发 880 840
· Analysis · 需求分析 (包括学习新技术) 220 250
· Design Spec · 生成设计文档 50 30
· Design Review · 设计复审 30 20
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 30 25
· Design · 具体设计 50 30
· Coding · 具体编码 200 205
· Code Review · 代码复审 80 40
· Test · 测试(自我测试,修改代码,提交修改) 100 150
Reporting 报告 90 90
· Test Report · 测试报告 20 20
· Size Measurement · 计算工作量 20 10
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 50 60
· 合计 990 940

运行环境

VisualStudio 2022

效能分析

程序中耗时最长的函数为 Generation:: IOsystem ,该函数集成了生成算式与存储至对应文件两个功能,主要性能瓶颈在于生成随机数与判断算式是否符合规范,即计算过程中不出现负数。

void Generation::IOsystem()
{
	Arithmetics A;
	std::ofstream outFile("exercisefile.txt");
	std::ofstream outFile_answer("Answer.txt");
	if (!outFile) {
		std::cerr << "无法打开输出文件." << std::endl;
		return;
	}
	if (!outFile_answer) {
		std::cerr << "无法打开输出文件." << std::endl;
		return;
	}
	for (int i = 0; i < request; i++){
		std::vector<int>numList = NumGeneration(numLimit);
		std::vector<char>operaList = OperatorGeneration();
		std::string expression = Combination(numList, operaList);
		if (expression.size()>3){
			std::random_device rd;
			std::mt19937 gen(rd());
			std::uniform_int_distribution<int> dist(0, 1);
			int isParentheses = dist(gen); // 随机生成 0 或 1
			if (isParentheses == 1){
				std::random_device rd;
				std::mt19937 gen(rd());
				std::uniform_int_distribution<int> dist(0, 3);
				int parenthesesPosition = dist(gen) * 2;
				while (parenthesesPosition > expression.size() - 2)
				{
					parenthesesPosition = parenthesesPosition - 2;
				}
				expression.insert(parenthesesPosition,1, '(');
				expression.insert(parenthesesPosition + 4, 1, ')');
			}
		}
		A.string_without_space_to_equation(expression);
		std::string res = A.get_result().to_string();
		if (A.get_result().numerator > 729||A.get_result().denominator>729) {
			i--;
			A.clear();
			continue;
		}
		outFile << i + 1 << ". " << expression << "\n";
		outFile_answer << i + 1 << ". " << res << "\n";
		A.clear();
	}
	outFile.close();
	outFile_answer.close();
}

改进方案

  1. 批量生成随机数:批量生成一些随机数,然后在循环中使用这些随机数。这可以减少生成器调用的次数。
  2. 多线程生成:使用多线程来并行生成随机数以提高生成的效率。
  3. 硬件随机数生成器:计算机系统提供的硬件随机数生成器速度更快。

设计实现过程

在类Arithmetic中内置了三个函数

void string_to_equation(std::string str);
void string_with_space_to_equation(std::string str);
void string_without_space_to_equation(std::string str);

用来实现将字符串格式的算式直接转换为Arithmetic类型,即将数字与算术符分别存入成员 std::vector<Number> nums std::vector<Operator> operators以进行下一步操作。

主函数流程图如下:
img

代码说明

Arithmetics::calculate

Number Arithmetics::calculate()
{
	if (nums.empty()) return Number{ 0 };
	auto it_num = nums.begin();
	auto it_opr = operators.begin();
	std::stack<Number> num_stack;
	std::stack<Operator> opr_stack;

	while (it_num != nums.end())
	{
		if (it_opr != operators.end() && it_opr->get_type() == LeftBrace)
		{
			num_stack.push(calculate_in_brace(it_num, ++it_opr));
		}
		else
		{
			if (it_num == nums.end()) break;
			num_stack.push(*it_num);
			++it_num;
		}

		if (it_opr == operators.end()) break;

		// if the operator stack is not empty and next operator's priority is less equal than previous operator's
		while (!opr_stack.empty() && it_opr->get_priority() <= opr_stack.top().get_priority())
		{
			Number& b = num_stack.top();
			num_stack.pop();
			Number& a = num_stack.top();
			num_stack.pop();

			num_stack.push(opr_stack.top().func(a, b));
			opr_stack.pop();
		}

		opr_stack.push(*it_opr);
		++it_opr;
	}
	if (num_stack.empty())
	{
		std::cerr << "ERROR : Empty result";
		return Number{ 0, 0 };
	}
	while (!opr_stack.empty())
	{
		Number& b = num_stack.top();
		num_stack.pop();
		Number& a = num_stack.top();
		num_stack.pop();

		num_stack.push(opr_stack.top().func(a, b));
		opr_stack.pop();
	}
	return num_stack.top();
}

​ 这段代码的目标是实现一个数学表达式的计算器,它通过遍历表达式中的数值和运算符,使用两个栈(num_stack 和 opr_stack)来帮助我们完成计算。

​ 代码的工作原理如下:

  1. 我们将数值压入 num_stack,将运算符压入 opr_stack。
  2. 当遇到左括号时,我们会调用 calculate_in_brace 函数来计算括号内的表达式,并将结果压入 num_stack。
  3. 当遇到右括号或者运算符优先级低于 opr_stack 栈顶运算符的优先级时,我们会从 num_stack 弹出两个数值和 opr_stack 弹出一个运算符,然后进行相应的运算,并将结果压入 num_stack。我们会一直重复这个过程,直到 opr_stack 为空或者遇到左括号。
  4. 最后,如果我们已经遍历完了 nums 和 operators,但 opr_stack 仍然不为空,那么我们会继续弹出 num_stack 的两个数值和 opr_stack 的一个运算符,进行相应的运算,并将结果压入 num_stack。这个过程会一直重复,直到 opr_stack 为空。
  5. 最终,num_stack 的栈顶元素就是整个表达式的计算结果,将其返回。

测试代码

控制台输入如下:

exercisefile.txt所存储的生成题目如下:

Answer.txt所存储的答案如下:

用户输入答案至answerfile.txt如下(将第十题答案修改为错误的值后):

成绩输入至Grade.txt如下:

项目小结

经验:

  • 密切的合作: 每天都保持沟通,确保项目进展顺利。
  • 技能互补: 拥有不同的技能和背景,我们能够相互补充,提供更全面的解决方案。
  • 任务分工明确: 减少重复工作和提高效率。

教训和挑战:

  • 时间管理: 在项目初期我们没有提前规划时间,导致项目的开工时间一再推迟。
  • 沟通障碍: 缺少面对面沟通,很多时候聊了半天才发现说的不是同一个模块。
  • 技术难题: 在生成算式的模块上,花费了较多时间。

结对感受:
我们在项目中面对了许多挑战,但通过相互的协作和努力,我们克服了这些问题,从中学到了很多。这个合作经验也让我更好地理解了团队合作的重要性。

结对闪光点和建议:

  • 沈纪康:我认为我们的合作非常成功,但我们可以进一步改进时间管理,以确保更好地满足项目截止日期。此外,我们可以更频繁地检查并确认项目细节,以减少误解。
  • 石云欣:同意,我们可以在时间管理方面做得更好。此外,我建议我们在项目的早期阶段进行更明确的分工,以便更好地理解彼此的工作内容。

总结:
这个结对项目对我们来说是一次宝贵的经验,我们在项目中不仅学到了新知识,还提高了团队合作和问题解决的能力。我们将这些经验应用到未来的项目中,继续共同努力,共同成长。

posted on 2023-09-28 23:04  toasty  阅读(57)  评论(0编辑  收藏  举报