结对项目
目录
结对项目
这个作业属于哪个课程 | https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/ |
---|---|
这个作业要求在哪里 | https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/homework/13230 |
这个作业的目标 | 实现一个自动生成小学四则运算题目的命令行程序 |
项目github地址 | github |
项目成员 | 黄博 |
1. PSP2.1表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 20 | 20 |
· Estimate | · 估计这个任务需要多少时间 | 265 | 255 |
Development | 开发 | 0 | 0 |
· Analysis | · 需求分析 (包括学习新技术) | 20 | 5 |
· Design Spec | · 生成设计文档 | 10 | 10 |
· Design Review | · 设计复审 (和同事审核设计文档) | 0 | 0 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 5 | 5 |
· Design | · 具体设计 | 10 | 10 |
· Coding | · 具体编码 | 120 | 120 |
· Code Review | · 代码复审 | 10 | 10 |
· Test | · 测试(自我测试,修改代码,提交修改) | 20 | 30 |
Reporting | 报告 | 20 | 20 |
· Test Report | · 测试报告 | 20 | 20 |
· Size Measurement | · 计算工作量 | 10 | 10 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 0 | 0 |
合计 | 265 | 255 |
2.性能分析
以上为自己创建式子的函数火焰图
可以发现大体时间用于创建式子,这里的创建平均时间复杂度为O(nlogn),每一次都使用随机决定数字或字符,在一个式子创建完成后丢入检测是否有子运算<0,如果有就重新生成
void create() { // 生成问题
vector<string> pro;
for (int i = 0; i < num; i++) {
string now = getstring();
now = pplus(now);
number tt = check(now);
while (tt.son < 0 || tt.mum < 0) {
now = getstring();
now = pplus(now);
tt = check(now);
}
pro.push_back(now);
}
std::ofstream file(outfile1);
if (!file.is_open()) {
cout << "Can't open file:" << outfile1 << endl;
exit(0);
}
for (int i = 0; i < pro.size(); i++) file << i + 1 << "." << pro[i] << endl;
}
string getstring() { // 生成每一个字符串
string now = "";
int cnt = prolen(sj) + 1;
for (int i = 0; i < cnt; i++) {
int num1 = promaxm(sj);
string op1 = op[proop(sj)];
now += to_string(num1);
if (i != cnt - 1) now += op1;
}
return now;
}
string pplus(string s) {
string now = "";
int cnt1 = 0;
for (int i = 0, flag = 1; i < s.size(); i++) {
if (isdigit(s[i])) {
if (flag) {
flag = 0;
if (plusop(sj) % 10 == 0) {
now += '(';
cnt1++;
}
}
} else {
if (!flag && cnt1 && plusop(sj) % 12 == 0) {
now += ')';
cnt1--;
}
flag = 1;
}
now += s[i];
}
for (int i = 0; i < cnt1; i++) {
now += ')';
}
// cout << now << endl;
return now;
}
number check(string s) {
// 获取操作符优先级
auto get_op = [&](char op1) {
if (op1 == '+' || op1 == '-') return 1;
if (op1 == '*' || op1 == '/') return 2;
return 0;
};
// 计算两个数的运算结果,同时判断是否为负数
auto cul = [&](number a, number b, char op1) {
number result(0, 1);
switch (op1) {
case '+':
result = a + b;
break;
case '-':
result = a - b;
break;
case '*':
result = a * b;
break;
case '/':
// 检查是否除以 0
if (b.son == 0) {
// cout << "Error: Division by zero!" << endl;
return number(0, 1); // 返回默认分数 0/1
}
result = a / b;
break;
default:
return number(0, 1); // 不应该到达这里,返回默认分数 0/1
}
if (result.son < 0) {
result.son = -1, result.mum = -1;
return result; // 如果结果为负,返回 (-inf, -inf)
}
return result;
};
stack<number> values; // 用于存储分数的栈
stack<char> ops; // 用于存储操作符的栈
for (int i = 0; i < s.size(); ++i) {
if (isdigit(s[i])) {
int num = 0;
while (i < s.size() && isdigit(s[i])) {
num = num * 10 + (s[i] - '0');
i++;
}
values.push(number(num, 1)); // 将数字作为分数推入栈,分母设置为 1
i--;
}
// 处理左括号
else if (s[i] == '(') {
ops.push('(');
} else if (s[i] == ')') {
while (!ops.empty() && ops.top() != '(') {
number val2 = values.top();
values.pop();
number val1 = values.top();
values.pop();
char op1 = ops.top();
ops.pop();
number result = cul(val1, val2, op1);
if (result.son < 0 || result.mum < 0) {
// cout << "a" << endl;
return result;
}
values.push(result); // 计算两个数并推入栈
}
ops.pop(); // 弹出左括号
}
// 处理操作符
else if (s[i] == '+' || s[i] == '-' || s[i] == '*' || s[i] == '/') {
while (!ops.empty() && get_op(ops.top()) >= get_op(s[i])) {
number val2 = values.top();
values.pop();
number val1 = values.top();
values.pop();
char op1 = ops.top();
ops.pop();
number result = cul(val1, val2, op1);
if (result.son < 0 || result.mum < 0) {
// cout << "b" << endl;
return result;
}
values.push(result);
}
ops.push(s[i]); // 将当前操作符压入栈
}
}
// 处理剩余操作符
while (!ops.empty()) {
number val2 = values.top();
values.pop();
number val1 = values.top();
values.pop();
char op1 = ops.top();
ops.pop();
number result = cul(val1, val2, op1);
if (result.son < 0 || result.mum < 0) {
// cout << "c" << endl;
return result;
}
values.push(result);
}
number final_result = values.top();
return final_result; // 返回最终的计算结果
}
3.设计实现过程
1.结构
1.number
number结构为分数结构,蕴含一个分子和分母,在里面重载了分数的加减乘除运算,方便简略后面的运算
2.函数
1.solve()
void solve(int flag)
solve接受一个flag的值表示是自己生成题目还是读取题目进行比较
2.create()
void create()
当flag=1时,被solve(1)调用,用于生成指定数量个四则运算
3.getstring()
string getstring()
被create调用,用于生成一个完整的字符串
4.pplus(string s)
string pplus(string s)
被create调用,接受一个字符串,为其添加括号
5.check(string s)
number check(string s)
接受一个字符串,返回四则运算的值
如果返回值分子分母都为-1,则说明存在子运算小于0,被create调用和被work调用用于计算
6.read(string s)
vector<string> read(string s)
接受一个文件名字符串,读入该文件,根据序号读出所有的四则运算,并返回
7. work(vector pro)
vector<string> work(vector<string> pro)
接受一个问题组,计算出结果之后返回一个答案组
8.write1(vector ans)
void write1(vector<string> ans)
接受答案组在自己生成时往文件中写入答案
9.compare(vector ans, vector ans1)
void compare(vector<string> ans, vector<string> ans1)
在solve(2)时对两个答案组进行答案匹配
10.write2(vector right, vector wrong)
void write2(vector<int> right, vector<int> wrong)
被compare调用,接受一个错误答案组和一个正确答案组,然后输出到grade中
3.流程图
4.代码说明
核心代码
1.number类
写了个构造函数和+-*/=的重载,方便运算
struct number {
int son, mum; // 构造函数
number(int n = 0, int d = 1) : son(n), mum(d) {
simplify();
}
void simplify() {
int g = gcd(abs(son), abs(mum));
son /= g, mum /= g;
if (mum < 0) {
son = -son;
mum = -mum;
}
}
number operator+(const number& other) const { // 分数加法
return number(son * other.mum + other.son * mum, mum * other.mum);
}
number operator-(const number& other) const { // 分数减法
return number(son * other.mum - other.son * mum, mum * other.mum);
}
number operator*(const number& other) const { // 分数乘法
return number(son * other.son, mum * other.mum);
}
number operator/(const number& other) const { // 分数除法
return number(son * other.mum, mum * other.son);
}
number& operator=(const number& other) {
if (this != &other) { // 防止自我赋值
son = other.son;
mum = other.mum;
simplify(); // 确保赋值后仍然是简化的形式
}
return *this;
}
};
2.check函数
用一个数字栈和一个符号栈进行模拟,遇到左括号就推入符号栈,遇到右括号就一直弹符号栈和数字栈进行运算,过程中遇到乘除法先计算.
最后栈中只会有加减法,依次运算即可
number check(string s) {
// 获取操作符优先级
auto get_op = [&](char op1) {
if (op1 == '+' || op1 == '-') return 1;
if (op1 == '*' || op1 == '/') return 2;
return 0;
};
// 计算两个数的运算结果,同时判断是否为负数
auto cul = [&](number a, number b, char op1) {
number result(0, 1);
switch (op1) {
case '+':
result = a + b;
break;
case '-':
result = a - b;
break;
case '*':
result = a * b;
break;
case '/':
// 检查是否除以 0
if (b.son == 0) {
// cout << "Error: Division by zero!" << endl;
return number(0, 1); // 返回默认分数 0/1
}
result = a / b;
break;
default:
return number(0, 1); // 不应该到达这里,返回默认分数 0/1
}
if (result.son < 0) {
result.son = -1, result.mum = -1;
return result; // 如果结果为负,返回 (-inf, -inf)
}
return result;
};
stack<number> values; // 用于存储分数的栈
stack<char> ops; // 用于存储操作符的栈
for (int i = 0; i < s.size(); ++i) {
if (isdigit(s[i])) {
int num = 0;
while (i < s.size() && isdigit(s[i])) {
num = num * 10 + (s[i] - '0');
i++;
}
values.push(number(num, 1)); // 将数字作为分数推入栈,分母设置为 1
i--;
}
// 处理左括号
else if (s[i] == '(') {
ops.push('(');
} else if (s[i] == ')') {
while (!ops.empty() && ops.top() != '(') {
number val2 = values.top();
values.pop();
number val1 = values.top();
values.pop();
char op1 = ops.top();
ops.pop();
number result = cul(val1, val2, op1);
if (result.son < 0 || result.mum < 0) {
// cout << "a" << endl;
return result;
}
values.push(result); // 计算两个数并推入栈
}
ops.pop(); // 弹出左括号
}
// 处理操作符
else if (s[i] == '+' || s[i] == '-' || s[i] == '*' || s[i] == '/') {
while (!ops.empty() && get_op(ops.top()) >= get_op(s[i])) {
number val2 = values.top();
values.pop();
number val1 = values.top();
values.pop();
char op1 = ops.top();
ops.pop();
number result = cul(val1, val2, op1);
if (result.son < 0 || result.mum < 0) {
// cout << "b" << endl;
return result;
}
values.push(result);
}
ops.push(s[i]); // 将当前操作符压入栈
}
}
// 处理剩余操作符
while (!ops.empty()) {
number val2 = values.top();
values.pop();
number val1 = values.top();
values.pop();
char op1 = ops.top();
ops.pop();
number result = cul(val1, val2, op1);
if (result.son < 0 || result.mum < 0) {
// cout << "c" << endl;
return result;
}
values.push(result);
}
number final_result = values.top();
return final_result; // 返回最终的计算结果
}
5.项目小结
看了下这次的作业虽然要求结对,但是仔细看了眼要求发现是一个模拟题,所以直接一个人做了.
主要遇到的问题在于以前没有试着不那么丑陋的写一个大模拟的代码,所以在整个依托代码分化为各个函数时怎么分化好看点用了点时间
由于一个人做完了,没什么结对感受