结对对象:徐劭斌
要求1 参考《构建之法》第4章两人合作,结对编程上述功能,要求每人发布随笔1篇 (代码是共同完成的,博客是分别完成的)。 (1) 给出每个功能的重点、难点、编程收获。(2)给出结对编程的体会,以及 (3) 至少5项在编码、争论、复审等活动中花费时间较长,给你较大收获的事件。 (10分)
(1)功能一 四则运算
重点:题目的生成,题目的计算,满足输入输出格式
难点:对异常题目的处理
首先随机生成四个数字并随机选择三个运算符,其中这里最初考虑的是为了防止除法除不尽或除以零的问题,将除数限定在1,2,4,5,8,但是在完成后面的功能后这一限定已经不需要了。
1 for (i = 0; i < nn; i++) 2 { 3 num[0] = rd.Next(0, 10); 4 num[1] = rd.Next(0, 10); 5 num[2] = rd.Next(0, 10); 6 num[3] = rd.Next(0, 10); //随机生成四个数 7 for (j = 0; j < 3; j++) 8 { 9 int n = rd.Next(0, 4); 10 charnum[j] = n; 11 if (n == 3) //如果有除法,出发后面的数字在1,2,4,5,8里面选择。 12 { 13 int[] divisor = { 1, 2, 4, 5, 8 }; 14 Random rd1 = new Random(); 15 num[j + 1] = divisor[rd1.Next(0, 5)]; 16 } 17 }
计算部分,使用了逆波兰将中序算式转换为后序,并使用操作符栈和结果队列进行计算。这一部分与功能二使用了同一函数,所以也考虑了括号的问题。
public static Queue<object> MidOrderToPostOrder(string expression) { var result = new Queue<object>(); var operatorStack = new Stack<char>(); operatorStack.Push('#'); char top, cur, tempChar; string tempNum; if (expression[0] == '-') expression = '0' + expression; for (int i = 0, j; i < expression.Length; ) { cur = expression[i++]; top = operatorStack.Peek(); if (cur == '(') { operatorStack.Push(cur); } else { if (IsOperator(cur)) { while (IsOperator(top) && ((IsLeftAssoc(cur) && priorities[cur] <= priorities[top])) || (!IsLeftAssoc(cur) && priorities[cur] < priorities[top])) { result.Enqueue(operatorStack.Pop()); top = operatorStack.Peek(); } operatorStack.Push(cur); } else if (cur == ')') { while (operatorStack.Count > 0 && (tempChar = operatorStack.Pop()) != '(') { result.Enqueue(tempChar); } } else { tempNum = "" + cur; j = i; while (j < expression.Length && (expression[j] == '.' || (expression[j] >= '0' && expression[j] <= '9'))) { tempNum += expression[j++]; } i = j; result.Enqueue(tempNum); } } } while (operatorStack.Count > 0) { cur = operatorStack.Pop(); if (cur == '#') continue; if (operatorStack.Count > 0) { top = operatorStack.Peek(); } result.Enqueue(cur); } return result; }
功能二 四则运算(带括号)
重点:括号生成的合法性与匹配
难点:因为括号的引入所产生的除法异常问题。
对于括号的加入,并没有想到太好的算法,所以采用了比较笨的办法,即列出了十种方案,在出每一道题时随机选择一种
expression[0] = "(" + num[0] + "" + fourOptions[charnum[0]] + num[1] + ")" + fourOptions[charnum[1]] + num[2] + fourOptions[charnum[2]] + num[3]; expression[1] = "(" + num[0] + "" + fourOptions[charnum[0]] + num[1] + fourOptions[charnum[1]] + num[2] + ")" + fourOptions[charnum[2]] + num[3]; expression[2] = num[0] + "" + fourOptions[charnum[0]] + "(" + num[1] + fourOptions[charnum[1]] + num[2] + ")" + fourOptions[charnum[2]] + num[3]; expression[3] = num[0] + "" + fourOptions[charnum[0]] + "(" + num[1] + fourOptions[charnum[1]] + num[2] + fourOptions[charnum[2]] + num[3] + ")"; expression[4] = num[0] + "" + fourOptions[charnum[0]] + num[1] + fourOptions[charnum[1]] + "(" + num[2] + fourOptions[charnum[2]] + num[3] + ")"; expression[5] = "(" + num[0] + "" + fourOptions[charnum[0]] + num[1] + ")" + fourOptions[charnum[1]] + "(" + num[2] + fourOptions[charnum[2]] + num[3] + ")"; expression[6] = "((" + num[0] + "" + fourOptions[charnum[0]] + num[1] + ")" + fourOptions[charnum[1]] + num[2] + ")" + fourOptions[charnum[2]] + num[3]; expression[7] = "(" + num[0] + "" + fourOptions[charnum[0]] + "(" + num[1] + fourOptions[charnum[1]] + num[2] + "))" + fourOptions[charnum[2]] + num[3]; expression[8] = num[0] + "" + fourOptions[charnum[0]] + "((" + num[1] + fourOptions[charnum[1]] + num[2] + ")" + fourOptions[charnum[2]] + num[3] + ")"; expression[9] = num[0] + "" + fourOptions[charnum[0]] + "(" + num[1] + fourOptions[charnum[1]] + "(" + num[2] + fourOptions[charnum[2]] + num[3] + "))"; int mm = rd.Next(0, 10);
因为引入了括号,导致功能一中的限定变得不太有效,有可能会出现2/(2-2)这样的非法式子,所以考虑在运算时发现这样的式子就重新生成。
public static double Compute(double leftNum, double rightNum, char op) { switch (op) { case '+': return leftNum + rightNum; case '-': return leftNum - rightNum; case '*': return leftNum * rightNum; case '/': double m = leftNum / rightNum; string s = m.ToString(); if (s.Length > 9) { go = 1; } return leftNum / rightNum; case '%': return leftNum % rightNum; case '^': return Math.Pow(leftNum, rightNum); default: return 0; }
定义了一个全局变量go来控制题目的重新生成。首先将局部变量m的值设置为一个多位的小数,而若是除数为零或是除得的结果小数位过长则返回0,并将go置1重新出题
功能三
重点:生成txt,避免重复
难点:无
功能三增加了参数,可以控制题目的数量并将题目输入到TXT中进行打印,比需要避免重复。这里避免重复使用的方法是比较题目的结果,若是结果重复则判断为重复的式子,重新生成。因为逻辑比较简单,就不再赘述了。
编程收获:首先是学习了使用逆波兰来计算表达式,同时练习了堆栈的使用,复习了文件的相关操作。特别是中序变后序的部分,了解原理就需要讨论很长时间,在了解原理后将它实现依旧不是一件容易的事情。
(2)结对编程的体会
首先阅读了《构建之法》 的相关章节,了解到了结对编程需要两个人共同思考,同时也需要角色互换。在这个过程中,首先是编程的准确率大大提高。以前的编程时,经常过度依赖编译器,写完程序先运行一下,出现问题再改,而在结对编程过程中,出现的问题很大概率会被直接指出来或者是在编程之前就讨论好,这使得出现的错误数量大幅下降,节省了大量时间,因为改正一个错误往往是编程中耗时最多的工作。同时,面对一个问题,两个人的思路明显比一个人更为宽广,就算是面对一个两个人都不懂的问题,讨论也能快速的得到结果。
(3)
逆波兰的原理与实现,这是整个编码中耗时最长的部分。
避免除法的异常问题,这一部分在功能一与功能二中都出现了,使用了不同的方法解决。在以后应该充分考虑整个题目,避免这种二次返工的现象。
括号的生成,一直想想一个比较合理的算法来解决这个问题,浪费了一些时间,最后按照队友的方法使用了给出10种选择的方式
避免题目重复,与上一个一样,最终规避了这个问题。
讨论功能四是否实现,这或许是结对编程的一个弊端,两个人如果都不想写,就一拍即合,放弃了实现这个功能。
要求2 给出照片1张,包括结对的2位同学、工作地点、计算机,可选项包括其他能表达结对编程工作经历的物品或场景。
工作地点:计算机楼二楼
计算机: 队友的笔记本电脑
要求3 使用coding.net做版本控制。checkin 前要求清理 临时文件、可执行程序,通常执行 build-clean可以达到效果。(5分)