软工第三次作业——结对项目
软工第三次作业
这个作业属于哪个课程 | 班级链接 |
---|---|
这个作业要求在哪里 | 作业要求 |
这个作业的目标 | 实现一个自动生成小学四则运算题目的命令行程序 |
姓名 | 学号 | github地址 |
---|---|---|
黎火坤 | 3123004310 | https://github.com/Keygener53/3123004310-project2 |
高泽彤 | 3123004304 | https://github.com/Tomgao4116/3123004304 |
1、PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | ·计划 | 30 | 40 |
· Estimate | · 估计这个任务需要多少时间 | 50 | 50 |
Development | · 开发 | 200 | 300 |
· Analysis | · 需求分析(包括学习新技术) | 80 | 50 |
· Design Spec | · 生成设计文档 | 20 | 10 |
· Design Review | · 设计复审 | 30 | 30 |
· Coding Standard | · 代码规范(为目前的开发制定合适的规范) | 20 | 10 |
· Design | · 具体设计 | 30 | 40 |
· Coding | · 具体编码 | 30 | 30 |
· Code Review | · 代码复审 | 20 | 30 |
· Test | · 测试(自我测试,修改代码,提交修改) | 60 | 50 |
· Reporting | · 报告 | 20 | 20 |
· Test Report | · 测试报告 | 20 | 30 |
· Size Measurement | · 计算工作量 | 10 | 10 |
· Postmortem & Process Improvement Plan | · 事后总结,并提出过程改进计划 | 10 | 20 |
合计 | 630 | 720 |
2、效能分析
对比之前版本的改进思路:通过栈结构减少临时变量,并支持文件的批量输入输出,节省了内存空间与运行时间,算法的最坏时间复杂度从O(n^3) 提升到 O(n^2)。
该项目中性能消耗最大的是calculate函数,最坏情况的时间复杂度是O(n^2),平均时间复杂度是O(n log n)
3、设计实现过程
1、代码有一个ExpressionGenerator 类,它的功能是生成数学表达式
2、函数
主函数main(int argc, char argv[]) 作为程序入口点*
主要功能函数
- build(string x, string y)
· 构建表达式节点,处理数字、运算符和括号 - write()
· 解析输入字符串,构建表达式序列 - calculate()
· 核心计算函数,处理表达式求值 - calNumber(node pre, node v)
· 执行具体的数学运算(加减乘除) - reserve()
· 输出计算结果
辅助函数
- add(ll a, ll b, ll c, ll d) - 分数加法
- sub(ll a, ll b, ll c, ll d) - 分数减法
- mul(ll a, ll b, ll c, ll d) - 分数乘法
- mins(ll a, ll b, ll c, ll d) - 分数除法
- printHelp() - 打印使用帮助
- judging() - 评判结果统计
- clearSpace() - 清理临时数据
- solve() - 解决表达式计算问题
- readin(int argc, char* argv[]) - 处理命令行参数
4、代码说明
以下列举了几个主要函数代码
1、build处理表达式函数
点击查看代码
void build(string x,string y){
if(x=="("){ // 左括号节点
b.push_back({0,0,x,y});
}else if(x==")"){ // 右括号节点
if(y=="+"||y=="*"||y=="-"||y=="%"){
b.push_back({0,0,x,y});
}else{
b.push_back({0,0,x,"NAN"});
}
} //‘%’代表除法
else{
ll num=0;
vector<ll> tmp;
x+="/"; // 分隔符‘/’代表分数
// 解析数字(可能是整数或分数)
for(int i=0;i<x.size();i++){
if(x[i]>='0'&&x[i]<='9'){
num=num*10+x[i]-'0';
}else{
tmp.push_back(num);
num=0;
}
}
if(tmp.size()==1){ // 整数
if(!(y=="+"||y=="*"||y=="-"||y=="%")){
b.push_back({tmp[0],1ll,"NUM","NAN"});
}else{
b.push_back({tmp[0],1ll,"NUM",y});
}
}else{ // 分数
if(!(y=="+"||y=="*"||y=="-"||y=="%")){
b.push_back({tmp[0],tmp[1],"NUM","NAN"});
}else{
b.push_back({tmp[0],tmp[1],"NUM",y});
}
}
}
}
2、calculate计算函数
点击查看代码
//核心计算函数,使用栈计算表达式值
void calculate(){
for(auto v:b){ // 遍历所有表达式节点
if(!st.size()){ // 栈空,直接压入
st.push(v);
}else{
// 尝试计算栈顶的两个数字节点
if(st.size()>1){
node x1=st.top();
st.pop();
node x2=st.top();
if(x1.st=="NUM"&&x2.st=="NUM"){ // 都是数字,可以计算
node ans=calNumber(x2,x1);
st.push(ans);
}else{ // 恢复栈状态
st.push(x1);
}
}
node pre=st.top(); // 获取栈顶元素
if(v.st=="("){ // 左括号,压入栈
st.push(v);
}else if(v.st==")"){ // 右括号,弹出直到左括号
node num=st.top(); // 数字
st.pop();
st.pop(); // 左括号
st.push({num.u,num.d,"NUM",v.nxt}); // 数字带操作符
}else{ // 数字节点,进行计算
node ans=calNumber(pre,v);
if(ans.u<0)tag2=1; // 标记负数
st.push(ans);
}
}
}
3、readin处理命令行函数
点击查看代码
//处理从命令行读入数据 +生成数据:
int readin(int argc,char* argv[]){
int n = 0;
int r = 0;
bool hasR = false;
// 解析命令行参数
for (int i = 1; i < argc; i++) {
std::string arg = argv[i];
if (arg == "-n" && i + 1 < argc) {
n = std::stoi(argv[++i]);
} else if (arg == "-r" && i + 1 < argc) {
r = std::stoi(argv[++i]);
hasR = true;
} else if (arg == "-h" || arg == "--help") {
printHelp();
return 0;
}
}
// 检查必要参数
if (!hasR) {
std::cout << "Error: -r parameter is required!" << std::endl;
printHelp();
return 1;
}
if (n <= 0) {
std::cout << "Error: -n parameter must be positive!" << std::endl;
printHelp();
return 1;
}
if (r <= 1) {
std::cout << "Error: -r parameter must be greater than 1!" << std::endl;
printHelp();
return 1;
}
// 生成表达式
ExpressionGenerator generator(r);
std::vector<Expression*> expressions = generator.generateExpressions(n);
ofstream outfile("F:/homework3/Exercises.txt");
// 输出结果(新格式:没有等号)
for (size_t i = 0; i < expressions.size(); ++i) {
std::cout<<i+1<< " : " << expressions[i]->toString() << std::endl;
outfile<< expressions[i]->toString()<<std::endl;
}
std::cout<<"数据已保存至:Exercises.txt文件中"<<'\n';
// 清理内存
for (Expression* expr : expressions) {
delete expr;
}
outfile.close();
}
5、测试运行
1、20个测试用例
2、程序正确性解释
1.使用正确的数学表达式思路,正确处理运算符优先级
void calculate() {
// 通过栈结构处理运算符优先级:
// 1. 高优先级(*,%)立即计算
// 2. 低优先级(+,-)在适当时候计算
// 3. 括号改变计算顺序
}
2.进行了各种边界情况处理
除零保护
点击查看代码
pll mins(ll a,ll b,ll c,ll d) {
// 由于ExpressionGenerator保证生成合法表达式,不会出现d=0的情况
ll up = b*c;
ll down = a*d; // 如果d=0,这里会崩溃,但生成器保证不会发生
ll g = __gcd(up,down);
up/=g,down/=g;
return {up,down};
}
负数检测
点击查看代码
node ans = calNumber(pre,v);
if(ans.u<0)tag2=1; // 检测到负数,标记为不合法
零值处理
点击查看代码
if(answer.u==0) { // 结果为0
std::cout<<cnt<<" : "<<0<<'\n';
// 正确输出0,而不是0/1等形式
}
3.有错误处理机制
参数验证
点击查看代码
if (r <= 1) {
std::cout << "Error: -r parameter must be greater than 1!" << std::endl;
printHelp();
return 1; // 正确退出并提示
}
表达式合法性检查
点击查看代码
if(tag2) { // 有负数,表达式不合法
std::cout<<cnt<<" : "<<"算式不合法!"<<'\n';
return; // 跳过当前题目的正常输出
}
6、项目小结
该结对项目是一个基于C++开发的四则运算表达式生成与计算系统,由两名成员合作完成。系统具备表达式自动生成、智能计算和结果验证等功能,支持分数运算和括号优先级处理。
1.合作分工:
一人负责算法核心开发和代码编写,另一人负责代码检查和博客撰写。
2.本次结对项目途中有不少挑战,主要包括以下几个:
· 挑战1:分数运算的精度问题
· 解决方案:采用分子分母分别存储,避免浮点数误差
· 挑战2:运算符优先级处理
· 解决方案:设计nxt字段标记下一个操作符,实现延迟计算
· 挑战3:负数结果检测
· 解决方案:在计算过程中实时监
3.未来的改进方向:
- 性能优化:支持更大规模的表达式计算
- 功能扩展:增加更多运算符和函数支持
- 用户体验:改进命令行界面,增加交互模式
- 测试完善:增加单元测试和性能测试
通过本次合作项目,我们不仅成功开发了一个功能完整的四则运算系统,更重要的是在实践中学习了团队协作、软件设计和算法实现的宝贵经验。项目的成功得益于明确的分工、良好的沟通和对技术细节的深入钻研。这个系统展示了如何将理论知识转化为实际可用的软件产品,为我们未来的软件开发工作奠定了坚实的基础。