作业要求:https://edu.cnblogs.com/campus/nenu/SWE2017FALL/homework/997
结对对象:任思佳
教师要求功能:1.支持出题4个数的四则运算题目,所有题目要求作者有能力正确回答
2.支持括号
3.限定题目数量,"精美"打印输出,避免重复
4.支持分数出题和运算
我的实现:功能一、二、三
本博客主要包含以下五点:
1.功能的重点难点及运行截图
2.在编码、争论、复审等活动中花费时间较长,给我较大收获的五事件
3.结对编程照片
4.结对编程体会
5.版本控制
一、功能的重点难点及运行截图
- 功能一
重点:生成随机数
使用方法:
#include <time.h> //使用当前时钟做种子 char num[] = { '41','42','43','44','45' }; srand((unsigned)time(NULL)); //初始化随机数 for (i = 0; i < 5; i++) //产生随机数 { n = rand() % 5; //取0到4的随机数 nnum[i] = num[n]; }
让时间当种子,这样srand每次返回的值就是不相同的。随机生成操作符用到的也是这个方法。由于后续方便随机数数组与操作符数组的结合,所以在此把随机数数组定义为char型,如41转换为字符为整型1。
重点:两个数组结合,输出运算表达式
实现方法:
narr[0] = nnum[0]; narr[1] = opt[0]; narr[2] = nnum[1]; narr[3] = opt[1]; narr[4] = nnum[2]; narr[5] = opt[2]; narr[6] = nnum[3]; narr[7] = '\0'; printf("%-15s", narr);
可以输出简单的表达式,不含括号。
重点:不含括号的混合计算(这个方法简单粗暴,功能二中有改进)
实现方法:
/* 计算,先找到'*'或'/',然后再算'+'或'-',比较运算符优先级 若第一个运算符为'*'或'/',不用特意判断第二个运算符 若第一个运算符为'+'或'-',需判断第二个运算符,以此类推 */ if (opt[0] == '*' || opt[0] == '/') { if (opt[0] == '*') { r1 = num[0] * num[1]; if (opt[1] == '*') { r2 = r1 * num[2]; if (opt[2] == '*') r3 = r2 * num[3]; else if (opt[2] == '/') r3 = r2 / num[3]; else if (opt[2] == '+') r3 = r2 + num[3]; else r3 = r2 - num[3]; } else if (opt[1] == '/') { r2 = r1 / num[2]; if (opt[2] == '*') r3 = r2 * num[3]; else if (opt[2] == '/') r3 = r2 / num[3]; else if (opt[2] == '+') r3 = r2 + num[3]; else r3 = r2 - num[3]; } else if (opt[1] == '+') { if (opt[2] == '*') r2 = num[2] * num[3]; else if (opt[2] == '/') r2 = num[2] / num[3]; else if (opt[2] == '+') r2 = num[2] + num[3]; else r2 = num[2] - num[3]; r3 = r1 + r2; } else { if (opt[2] == '*') { r2 = num[2] * num[3]; r3 = r1 - r2; } else if (opt[2] == '/') { r2 = num[2] / num[3]; r3 = r1 - r2; } else if (opt[2] == '+') { r2 = num[2] + num[3]; r3 = r1 - r2; } else r3 = r1 - num[2] - num[3]; } } if (opt[0] == '/') { r1 = num[0] / num[1]; if (opt[1] == '*') { r2 = r1 * num[2]; if (opt[2] == '*') r3 = r2 * num[3]; else if (opt[2] == '/') r3 = r2 / num[3]; else if (opt[2] == '+') r3 = r2 + num[3]; else r3 = r2 - num[3]; } else if (opt[1] == '/') { r2 = r1 / num[2]; if (opt[2] == '*') r3 = r2 * num[3]; else if (opt[2] == '/') r3 = r2 / num[3]; else if (opt[2] == '+') r3 = r2 + num[3]; else r3 = r2 - num[3]; } else if (opt[1] == '+') { if (opt[2] == '*') r2 = num[2] * num[3]; else if (opt[2] == '/') r2 = num[2] / num[3]; else if (opt[2] == '+') r2 = num[2] + num[3]; else r2 = num[2] - num[3]; r3 = r1 + r2; } else { if (opt[2] == '*') r2 = num[2] * num[3]; else if (opt[2] == '/') r2 = num[2] / num[3]; else if (opt[2] == '+') r2 = num[2] + num[3]; else r2 = num[2] - num[3]; r3 = r1 - r2; } } } //第一个运算符为'+'或'-',需判断第二个运算符 if (opt[0] == '+' || opt[0] == '-') { if (opt[0] == '+') { if (opt[1] == '*') { r1 = num[1] * num[2]; if (opt[2] == '*') r2 = r1 * num[3]; else if (opt[2] == '/') r2 = r1 / num[3]; else if (opt[2] == '+') r2 = r1 + num[3]; else r2 = r1 - num[3]; r3 = r2 + num[0]; } else if (opt[1] == '/') { r1 = num[1] / num[2]; if (opt[2] == '*') r2 = r1 * num[3]; else if (opt[2] == '/') r2 = r1 / num[3]; else if (opt[2] == '+') r2 = r1 + num[3]; else r2 = r1 - num[3]; r3 = r2 + num[0]; } else if (opt[1] == '+') { if (opt[2] == '*') { r1 = num[2] * num[3]; r3 = r1 + num[0] + num[1]; } else if (opt[2] == '/') { r1 = num[2] / num[3]; r3 = r1 + num[0] + num[1]; } else if (opt[2] == '+') r3 = num[0] + num[1] + num[2] + num[3]; else r3 = num[0] + num[1] + num[2] - num[3]; } else { if (opt[2] == '*') { r1 = num[2] * num[3]; r3 = num[0] + num[1] - r1; } else if (opt[2] == '/') { r1 = num[2] / num[3]; r3 = num[0] + num[1] - r1; } else if (opt[2] == '+') r3 = num[0] + num[1] - num[2] + num[3]; else r3 = num[0] + num[1] - num[2] - num[3]; } } if (opt[0] == '-') { if (opt[1] == '*') { r1 = num[1] * num[2]; if (opt[2] == '*') r3 = num[0] - r1 * num[3]; else if (opt[2] == '/') r3 = num[0] - r1 / num[3]; else if (opt[2] == '+') r2 = num[0] - r1 + num[3]; else r3 = num[0] - r1 - num[3]; } else if (opt[1] == '/') { r1 = num[1] / num[2]; if (opt[2] == '*') r2 = r1 * num[3]; else if (opt[2] == '/') r2 = r1 / num[3]; else if (opt[2] == '+') r2 = r1 + num[3]; else r2 = r1 - num[3]; r3 = num[0] - r2; } else if (opt[1] == '+') { if (opt[2] == '*') { r1 = num[2] * num[3]; r3 = num[0] - num[1] + r1; } else if (opt[2] == '/') { r1 = num[2] / num[3]; r3 = num[0] - num[1] + r1; } else if (opt[2] == '+') r3 = num[0] - num[1] + num[2] + num[3]; else r3 = num[0] - num[1] + num[2] - num[3]; } else { if (opt[2] == '*') { r1 = num[2] * num[3]; r3 = num[0] - num[1] - r1; } else if (opt[2] == '/') { r1 = num[2] / num[3]; r3 = num[0] - num[1] - r1; } else if (opt[2] == '+') r3 = num[0] - num[1] - num[2] + num[3]; else r3 = num[0] - num[1] - num[2] - num[3]; } } }
运行截图:
- 功能二
重点:添加括号
实现方法:
//生成带括号或不带括号的表达式 if (((opt[0] == '*' || opt[0] == '/') && (opt[1] == '+' || opt[1] == '-')) || ((opt[2] == '*' || opt[2] == '/') && (opt[1] == '+' || opt[1] == '-'))) { n = rand() % 2; if (n % 2 == 0) { narr[0] = nnum[0]; narr[1] = opt[0]; narr[2] = '('; narr[3] = nnum[1]; narr[4] = opt[1]; narr[5] = nnum[2]; narr[6] = ')'; narr[7] = opt[2]; narr[8] = nnum[3]; narr[9] = '\0'; printf("%-15s", narr); inFix2PostFix(narr, postFix); result = postFixEval(postFix); //printf("%d", result); if (result - ((int)result) == 0) //若小数点后数字全为0,则输出为整型 printf("%d", (int)result); else printf("%.2f", result); } else { narr[0] = nnum[0]; narr[1] = opt[0]; narr[2] = nnum[1]; narr[3] = opt[1]; narr[4] = nnum[2]; narr[5] = opt[2]; narr[6] = nnum[3]; narr[7] = '\0'; printf("%-15s", narr); inFix2PostFix(narr, postFix); result = postFixEval(postFix); //printf("%d", result); if (result - ((int)result) == 0) printf("%d", (int)result); else printf("%.2f", result); } }
我没有想到随机实现括号位置的固定,所以对于括号的出现位置,我选择固定,然后产生一个随机数,若这个数为偶数,括号出现,反之没有括号。代码里还有三个循环体没有贴出。
重点:栈
实现方法:
//判断字符是否为操作符 bool IsOperator(char c) { char op[] = { '+', '-', '*', '/', '(', ')' };//操作符数组 for (int i = 0; i < sizeof(op); i++) { if (c == op[i]) return true; } return false; } // 比较两个操作符的优先级 int ComPriority(char op1, char op2) { if (op1 == '(') { return -1; } if (op1 == '+' || op1 == '-') { if (op2 == '*' || op2 == '/') { return -1; } else { return 0; } } if (op1 == '*' || op1 == '/') { if (op2 == '+' || op2 == '-') { return 1; } else { return 0; } } }
遇到数字压栈,遇到第二个操作符先和第一个操作符比较,然后计算结果压栈。
难点:逆波兰式(后缀表达式)
实现方法
// 中缀表达式转换成后缀表达式 void inFix2PostFix(char* inFix, char* postFix) { int j = 0, len; char c; stack<char> st; len = strlen(inFix); for (int i = 0; i < len; i++) { c = inFix[i]; if (c == '(') st.push(c); else if (c == ')') { while (st.top() != '(') { postFix[j++] = st.top(); st.pop(); } st.pop(); } else { if (!IsOperator(c)) st.push(c); else { while (st.empty() == false && ComPriority(st.top(), c) >= 0) { postFix[j++] = st.top(); st.pop(); } st.push(c); } } } while (st.empty() == false) { postFix[j++] = st.top(); st.pop(); } postFix[j] = 0; }
运行截图:
- 功能三
重点:又控制台输入题目数
实现方法:
int a = atoi(argv[2]);
二、在编码、争论、复审等活动中花费时间较长,给我较大收获的五事件
a 首先是使用什么语言,最后统一选择了两人都熟悉的c语言;
b 然后是制定代码规范过程中对于规范的条例有过观点不和,最后统一修正形成一套规范;
c 对于要使用的编程工具,我想要使用codeblocks,而她想要使用VS,也产生了一些分歧;
d 然后在编码过程中对于如何判断运算符号优先级,有过关于不同方法的争论,我曾经做过下图这样的尝试:最后还是达成了一致,使用了前文提到的方法;
e 在进行单元测试时,对于工具选择也有分歧,关于应该使用cunit,还是cppunit,单元测试环节耗费了大量的时间,安装工具并修改各种配置,步骤很繁琐,印象深刻。
三、结对编程照片
四、结对编程体会
五、版本控制