结对项目
这个作业属于哪个课程 | 软件工程 |
---|---|
这个作业要求在哪里 | 结对项目 |
这个作业的目标 | 合作完成一个随机生成四则运算题目的项目 |
姓名 | 郑统镇 | 郑品俊 |
---|---|---|
学号 | 3122004417 | 3122004416 |
1. github项目地址
2. PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 40 |
Estimate | 估计这个任务需要多少时间 | 300 | 400 |
Development | 开发 | 200 | 200 |
Analysis | 需求分析 (包括学习新技术) | 30 | 60 |
Design Spec | 生成设计文档 | 30 | 40 |
Design Review | 设计复审 | 10 | 10 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 10 | 20 |
Design | 具体设计 | 60 | 50 |
Coding | 具体编码 | 300 | 400 |
Code Review | 代码复审 | 30 | 60 |
Test | 测试(自我测试,修改代码,提交修改) | 30 | 40 |
Reporting | 报告 | 30 | 30 |
Test Repor | 测试报告 | 60 | 60 |
Size Measurement | 计算工作量 | 20 | 20 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 50 | 50 |
合计 | 1450 | 1400 |
3. 设计实现过程
总体设计
生成题目与结果模块
判断答案模块
4. 代码说明
数据的定义
typedef int Status;
typedef struct variable
{
int num_or_Symbol; //0是数字,1是符号
int Symbol; //+ - * % ( ) 分别表示为 0 1 2 3 4 5
int numer; //分子
int Den; //分母
int num; //系数
struct variable *next;
}var;
关键代码
//生成四则运算式
void generateExpression(var* head, int max)
{
int length = getRandom(2, 4); //生成的数字个数
var *p = head;
for(int i = 0; i < length; i++){
var *NumNode;
NumNode = (var*)malloc(sizeof(var));
if(getRandom(0 ,2)==0){
getFraction(max,NumNode);
}else{
getnum(max,NumNode);
}
p->next = NumNode;
p = NumNode;
NumNode->next = NULL;
}
p=head;
if(length >= 3){
int left = getRandom(1, length-1);
int right = getRandom(left+1, length);
var *templ;
templ=(var *) malloc(sizeof(var));
getparenthesis(templ, L);
for(int i = 1; i < left; i++){
p = p->next;
}
templ->next = p->next;
p->next = templ;
p = templ->next;
p=head->next;
var *tempr;
tempr=(var *) malloc(sizeof(var));
getparenthesis(tempr, R);
for(int i = 0; i < right; i++){
p = p->next;
}
tempr->next = p->next;
p->next = tempr;
p = tempr->next;
}
p=head->next;
while(p->next!=NULL){
if((p->num_or_Symbol==0&&p->next->num_or_Symbol==0) || (p->num_or_Symbol==0&&p->next->Symbol==4) || (p->Symbol==5&&p->next!=NULL&&p->next->num_or_Symbol==0))
{
var *SymbolNode;
SymbolNode = (var*)malloc(sizeof(var));
getRandomOperator(SymbolNode);
SymbolNode->next=p->next;
p->next=SymbolNode;
p= SymbolNode;
}
p=p->next;
}
}
//中缀表达式转后缀表达式
Status Infix_to_Postfix(var *p, var *Postfix) {
var* stack = NULL; // 定义一个栈,用于存储运算符
var* q = p->next; // 中缀表达式
Postfix->next = NULL;
// 遍历中缀表达式的每个元素
while (q != NULL) {
var* v = q;
q = q->next;
if (v->num_or_Symbol == 0) { // 如果是数字
// 将数字放入后缀表达式中
v->next = Postfix->next;
Postfix->next = v;
}
else if (v->num_or_Symbol == 1 && v->Symbol == 4) { // 如果是左括号
// 将左括号入栈
v->next = stack;
stack = v;
}
else if (v->num_or_Symbol == 1 && v->Symbol == 5) { // 如果是右括号
// 循环直到遇到左括号或栈为空
while (stack->Symbol != 4 && stack != NULL) {
var* s = stack;
stack = stack->next; // 将栈顶元素出栈
// 将栈顶元素放入后缀表达式中
s->next = Postfix->next;
Postfix->next = s;
}
if (stack == NULL) { // 如果栈为空,说明缺少左括号
free(stack); // 释放栈内存
return ERROR;
}
stack = stack->next; // 将左括号出栈
free(v); // 释放右括号节点内存
}
else { // 如果是运算符
// 当前运算符优先级小于等于栈顶运算符优先级时,将栈顶元素出栈并放入后缀表达式中
while (stack != NULL && prio(v) <= prio(stack)) {
var* s = stack;
stack = stack->next; // 将栈顶元素出栈
// 将栈顶元素放入后缀表达式中
s->next = Postfix->next;
Postfix->next = s;
}
// 将当前运算符入栈
v->next = stack;
stack = v;
}
}
// 将栈中剩余的运算符依次出栈并放入后缀表达式中
while (stack != NULL) {
var* s = stack;
stack = stack->next;
s->next = Postfix->next;
Postfix->next = s;
}
// 此处反转Postfix链表
var *l,*k;
l = Postfix -> next;
Postfix -> next = NULL;
while(l != NULL){
k = l->next;
l->next = Postfix->next;
Postfix->next = l;
l = k;
}
return SUCCESS;
}
// 计算后缀表达式
Status calc(var* Postfix, var* ans) {
var* stack = NULL; // 定义一个栈,用于存放操作数
var* postfix = Postfix->next;
while (postfix != NULL) { // 遍历后缀表达式
var* v = postfix;
postfix = postfix->next;
if (v->num_or_Symbol == 0) { // 如果是操作数
v->next = stack;
stack = v; // 将操作数压入栈中
} else { // 如果是运算符
if (stack == NULL || stack->next == NULL) { // 判断操作数是否足够两个,如果不足则报错
printf("发生错误,操作数不足,无法完成运算\n");
return ERROR;
}
var* ret;
ret = (var*)malloc(sizeof(var)); // 用于存储计算结果的节点
if (ret == NULL)
return OVERFLOW;
// 从栈中弹出两个操作数
var* operand2 = stack;
stack = stack->next;
var* operand1 = stack;
stack = stack->next;
// 执行运算操作
if (!operate(operand1, operand2, v, ret)) { // 如果计算过程中出现了负数或除以零的非法操作
// printf("计算过程中出现了负数或除以零的非法操作");
return ERROR; // 返回错误
}
// 将计算结果压入栈中
ret->next = stack;
stack = ret;
}
}
if (stack->next == NULL) // 最后栈中只剩下一个元素,即最终的计算结果
{
stack->num_or_Symbol = 0;
ans->next = stack; // 将最终的计算结果存储到 ans 中
return SUCCESS; // 返回成功标志
}
printf("计算结果错误\n");
return ERROR;
}
// 将表达式从字符串转换为链表表示
var* expressionToList(char* expression) {
var* head = newNode(-1, -1, -1, -1, -1); // 头节点
var* current = head;
int len = strlen(expression);
int i = 0;
while (i < len) {
if (expression[i] >= '0' && expression[i] <= '9') {
// 数字
int j = i;
int num = 0;
int numer = 0;
int Den = 0;
int n = 0;
// 解析整数部分
while (expression[j] >= '0' && expression[j] <= '9') {
n = n * 10 + expression[j] - '0';
j++;
}
num = n;
// 如果后面有带分数部分
if (expression[j] == '\'') {
j++; // 跳过分数符号
n = 0;
// 解析分子部分
while (expression[j] >= '0' && expression[j] <= '9') {
n = n * 10 + expression[j] - '0';
j++;
}
numer = n;
// 解析分母部分
if (expression[j] == '/') {
j++; // 跳过分数线
n = 0;
// 解析分母
while (expression[j] >= '0' && expression[j] <= '9') {
n = n * 10 + expression[j] - '0';
j++;
}
Den = n;
}
}
// 如果后面有真分数部分
else if (expression[j] == '/') {
j++; // 跳过分数线
numer = num;
num = 0;
n = 0;
// 解析分子
while (expression[j] >= '0' && expression[j] <= '9') {
n = n * 10 + expression[j] - '0';
j++;
}
Den = n;
//解析分母
}
// 创建节点并连接链表
current->next = newNode(0, -1, num, numer, Den);
current = current->next;
i = j - 1; // 更新索引
} else if (expression[i] == '+') {
current->next = newNode(1, 0, -1, -1, -1);
current = current->next;
} else if (expression[i] == '-') {
current->next = newNode(1, 1, -1, -1, -1);
current = current->next;
} else if (expression[i] == '*') {
current->next = newNode(1, 2, -1, -1, -1);
current = current->next;
} else if (expression[i] == '%') {
current->next = newNode(1, 3, -1, -1, -1);
current = current->next;
} else if (expression[i] == '(') {
current->next = newNode(1, 4, -1, -1, -1);
current = current->next;
} else if (expression[i] == ')') {
current->next = newNode(1, 5, -1, -1, -1);
current = current->next;
}
i++;
}
return head;
}
5. 测试运行
随机生成10道题目
改变题目数值后随机生成10000道题目
在exercise.txt文件内填入答案后判断对错
6. 项目小结
1.结对合作可以实现高效沟通,互相交换想法,讨论算法,了解对方的思维方式,从而获得启发,感谢搭档帮助,收货颇丰。
2.通过本项目的实践,我掌握了算术表达式的生成、中缀表达式转后缀表达式的方法、计算后缀表达式的规则等相关技术。在项目的实现过程中,我遇到了诸多挑战,如如何实现命令行参数解析、如何找错修改bug等,但通过不断调试和优化,最终完成了项目的设计和实现。同时,通过本项目,我也加深了对 C 语言的理解和应用,提高了编程能力和实际问题解决能力。我相信这些经验和技能对我的日后学习和工作都将有所帮助。