这次和代秋彤同学结对编写四则运算这一题目,之前从来没有和别人结对编程过,感觉也挺有趣的。综合了两个人的编程能力等各方面因素,选择了用c语言来完成。
要求1 参考《构建之法》第4章两人合作,结对编程上述功能,要求每人发布随笔1篇 (代码是共同完成的,博客是分别完成的)。 (1) 给出每个功能的重点、难点、编程收获。(2)给出结对编程的体会,以及 (3) 至少5项在编码、争论、复审等活动中花费时间较长,给你较大收获的事件。 (10分)
功能1的重点难点是生成随机数,让时间当种子,这样srand每次返回的值就是不相同的。随机生成操作符用到的也是这个方法。由于后续方便随机数数组与操作符数组的结合,所以在此把随机数数组定义为char型,如41转换为字符为整型1。
#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]; }
重点:两个数组结合,输出运算表达式
实现方法:
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);
部分代码:
功能1:
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]; } } }
功能1运行结果:
功能2的重点和难点是判断运算符号的优先级吧,特别是功能2的括号,挺难插的。受到一个博客的启发,使用将中缀表达式转换成后缀表达式,再对后缀表达式求值的方法。使用这种方法是因为:1.中缀表达式必然存在后缀表达式 2.后缀表达式不存在优先级问题,只需利用栈进行“从左至右依次计算”即可。后缀表达式的计算方法,就是:将后缀表达式从左到右依次遍历,如果当前元素为数字则入(操作数)栈,如果为操作符,则pop出栈顶两个元素(第一次pop出的是右操作数,第二次pop出的是左操作数)进行运算,然后将计算结果再次入栈,直至表达式结束,此时操作数栈内理应只剩一个元素即表达式结果。
博客链接:
功能2:
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; } //后缀表达式求值 double postFixEval(char* postFix) { stack<char> st; int len = strlen(postFix); char c; for (int i = 0; i < len; i++) { c = postFix[i]; if (IsOperator(c) == false) { st.push(c - '0'); } else { char op1, op2; int val; op1 = st.top(); st.pop(); op2 = st.top(); st.pop(); switch (c) { case '+': val = op1 + op2; break; case '-': val = op2 - op1; break; case '*': val = op1 * op2; break; case '/': val = op2 / op1; break; } st.push(val); } } return st.top(); }
功能2运行结果:
功能3的重点和难点是命令行参数,支持手动输入题目数量:
部分代码:
功能3:
int main(int argc, char *argv[]) { int y; int rcount = 0; //rcount答对题目计数 int a = atoi(argv[2]); //传入的参数转成整数 if (!(a == 1 || a == 2 || a == 3 || a == 4 || a == 5 || a == 6 || a == 7 || a == 8 || a == 9 || a == 10 || a == 11 || a == 12 || a == 13 || a == 14 || a == 15 || a == 16 || a == 17 || a == 18 || a == 19 || a == 20)) { printf("题目数量必须是 正整数。"); exit(0); } for (y = 0; y < a; y++) { int i, j, n, op; float answer, result; char num[] = { '41','42','43','44','45' }; char operate[] = { '+', '-', '*', '/' }, opt[3]; char narr[] = { 0 }; char nnum[4]; char postFix[20]; //double answer; srand((unsigned)time(NULL)); //初始化随机数 for (i = 0; i < 4; i++) //产生随机数 { n = rand() % 5; //取0到4的随机数 nnum[i] = num[n]; } srand((unsigned)time(NULL)); for (j = 0; j < 4; j++) //从给定的四个运算符中随机产生三个相同或不同的运算符 { op = rand() % 4; opt[j] = operate[op]; } //生成带括号或不带括号的表达式 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); } } else if (opt[0] == '+' && (opt[1] == '*' || opt[1] == '/')) { narr[0] = '('; narr[1] = nnum[0]; narr[2] = opt[0]; narr[3] = nnum[1]; narr[4] = ')'; narr[5] = opt[1]; narr[6] = nnum[2]; 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) printf("%d", (int)result); else printf("%.2f", result); } else if ((opt[0] == '/'&&opt[1] == '-') || (opt[0] == '*'&&opt[1] == '+')) { narr[0] = '('; narr[1] = '('; narr[2] = nnum[0]; narr[3] = opt[0]; narr[4] = nnum[1]; narr[5] = ')'; narr[6] = opt[1]; narr[7] = nnum[2]; narr[8] = ')'; narr[9] = opt[2]; narr[10] = nnum[3]; narr[11] = '\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); } 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); } printf("\n"); //printf("?"); int ntime; ntime = 1000 + rand() % 1000; Sleep(ntime);
功能3运行结果:
要求2 给出照片1张,包括结对的2位同学、工作地点、计算机,可选项包括其他能表达结对编程工作经历的物品或场景。 (5分)
编程收获就是学到了很多新技能吧,对c语言有新的认识,可以说是一次巩固和发展的过程,编程过程中使用的知识点,好多都在书上读到过,在课堂上学习过,然而真的到了实际应用的时候,或者想不到使用,或者不知道如何具体应用,没有动手的经历,书本上的知识永远不能变成自己的,基础和实践同样重要。还有交到了新朋友,附上某一次的工作照:
结对编程的体会 就是两个人一起工作要比一个人轻松,有效率,而且集思广益,在解决问题时会得到更好的解决方案。在此感谢代秋彤同学对我的帮助,受益匪浅。
争执的点
a 首先是使用什么语言,最后统一选择了两人都熟悉的c语言;
b 然后是制定代码规范过程中对于规范的条例有过观点不和,最后统一修正形成一套规范;
c 对于要使用的编程工具,我想要使用codeblocks,而她想要使用VS,也产生了一些分歧;
d 然后在编码过程中对于如何判断运算符号优先级,有过关于不同方法的争论,我曾经做过下图这样的尝试:最后还是达成了一致,使用了前文提到的方法;
e 在进行单元测试时,对于工具选择也有分歧,关于应该使用cunit,还是cppunit,单元测试环节耗费了大量的时间,安装工具并修改各种配置,步骤很繁琐,印象深刻。