软件工程第3次作业
要求0:
作业地址:https://edu.cnblogs.com/campus/nenu/2016CS/homework/2266
要求1:
git地址: https://git.coding.net/Jingr98/f4.git
要求2:
1.结对编程同学的博客地址&姓名:https://www.cnblogs.com/Jingr/p/9924521.html 井冉
2.解题思路:
当初看到四则运算这个题目时,第一想法是先解决计算表达式这一块,再来增加生成表达式的功能,大致形成计算表达式(满足功能1)-->计算表达式(满足功能2和功能3)-->随机生成表达式的流程思路。于是先在网上查找了四则运算计算这一块的内容,发现大多会使用中缀表达式与后缀表达式来完成,并采用双栈(即操作数一个栈,运算符一个栈),从这篇博客我还了解到挺多的(https://blog.csdn.net/that163/article/details/7558268),代码简短易懂。其次就是随机生成四则运算题目,在网上查阅了很多博主写的代码,参考了他们的随机生成算式的思路如下:
- 随机生成一个数字,代表着生成表达式中操作数的个数。
- 循环生成一个数字,将其输出,然后等概率生成‘+’‘-’‘*’‘/’中的一个跟在该数字后面,输出。
- 以一定概率生成左括号,若生成了左括号则输出,并进行计数标志当前共有多少个未完成匹配的左括号。
- 若当前有未完成匹配的左括号,则在生成一个数字后,生成一个操作符前,以一定的概率生成右括号。
- 在生成完毕后,生成最后一个数并将为匹配的左括号予以匹配。
附上博客地址,希望对大家有用(https://www.cnblogs.com/fanfan-blogs/p/5246469.html)。
3.重点/难点
1)功能1:支持整数和不含括号的四则运算且表达式可以重复。
功能1只需要计算整数,所以唯一的难点是输出结果时的判断。当时没有读清题目,考虑不仔细,简单的判断若为整数,则原样输出,否则,保留3位小数,但发现会存在有可以被整除的小数,得到的结果可能是类似2.5,19.36等这样的形式,这时保留3位小数就会输出结果冗余。所以又改了思路,判断小数点后面的位数,如果位数超过3位,则保留3位输出,否则原样输出。附上判断小数位数代码。
函数point():判断小数点后面的位数是否超过3
/** *函数名:point() *函数功能:判断小数点后面的位数是否超过3 *param:double num */ int point(double num) { int i,f = 0; num *= 1000; if(num - (int)num > eps) f = 1; return f; }
2)功能2:支持小数和含小括号的四则运算且表达式可以重复。
功能2涉及到小数和小括号的运算,同样也涉及到了结果的输出以及和用户的结果进行对比,判断是否正确。由于要求只需保留到3位小数,故设置eps=1e-4,当用户结果与正确答案相差小于eps时,则判定回答正确。其次,是由于小括号的加入,所以还涉及到了更进一步的优先级计算,于是直接设置了判断优先级的函数,给6种运算符赋予优先级。最后就是由于小括号的加入,需要随机生成小括号,下附部分代码:
//小括号的随机生成 if( n < 3 && i < 6 && i > 0 ){ s += "("; ans1++; } s += tmp; if( n > 6 && i > 0 && i < 6){ if( ans1 > 0 ) ans1--; else ans2++; s += ")"; } } else { int n = rand() % 4; s += op[n]; } } sort( a , a + 4 ); pdd current = mk( a[0] , a[1] , a[2] , a[3] ); if( stPdd.find(current) != stPdd.end() ){ tmp++; return 1; } stPdd.insert( current ); string lft , rht; //保证小括号成对出现 for(int i = 0 ; i < ans2 ; i++ ) lft += "("; for(int i = 0 ; i < ans1 ; i++ ) rht += ")"; s = lft + s + rht;
3)功能3:表达式不重复且输出结果显示在控制台,且将控制台显示的结果输出到指定位置的txt文件中。
功能3其实就是在功能2的基础上增加表达式不重复且将结果输出到指定文件中的功能。在使生成的表达式不重复上,我感觉还是挺难的,因为你需要判断交换律、结合律、分配律等导致的重复的情况,比较复杂,所以这个地方是参考了别人的代码,然后是将控制台结果输出到txt文件里,在这里运用到了C++的ofstream,
ofstream fout( "d:\\mytest.txt" );
fout << "Learning C++ is very useful."<< endl;
搜索到了这样一篇博客,详细的讲解了ofstream与ifstream的用法(https://www.cnblogs.com/batman425/p/3179520.html)。
4)解决计算表达式的功能:利用双栈将中缀表达式转换成后缀表达式,遍历后缀表达式得到结果。此处通过双栈,分离开操作数与运算符,简化了计算,方便操作与判断。下附解决代码:
/** *函数名:work() *函数功能:利用双栈将中缀表达式转换成后缀表达式,遍历后缀表达式得到结果 *param:string s */ double work(string s ) { stack<double> sNum; //存储操作数的栈 stack<char> sOp; //存储运算符的栈 int i = 0, flag = 1; char c; double x, y; sOp.push('\0'); c = s[i]; while (flag) { if (c >= '0' && c <= '9' || c == '.') { sNum.push(TransToNum(s, i)); } else if (c == '\0' && sOp.top() == '\0') { flag = 0; } else if (c == '(' || (power(c) > power(sOp.top()))) { sOp.push(c); i++; } else if (c == ')'&& sOp.top() == '(') { //遇到一对括号,先将括号内算式进行计算 sOp.pop(); i++; } else if (power(c) <= power(sOp.top())) { x = sNum.top(); sNum.pop(); if(sNum.empty()) y = 0; else { y = sNum.top(); sNum.pop(); } c = sOp.top(); if( c == '/' && fabs( x ) < eps ){ int n = rand() % 3; c = op[n]; } sOp.pop(); switch (c) { case '+':y = x + y; break; case '-':y = y - x; break; case '*':y = x * y; break; case '/':y = y / x; break; } sNum.push(y); } c = s[i]; } return sNum.top(); }
4.编程收获:
这次编程题目看似只是简单地四则运算,但是由于涉及到众多要求,后期改善时发现了一些bug,比如小数位数的判断与处理,重复表达式的处理等等。从前期的思路搭建,到实际代码编写的落实再到后期bug调试解决,都让我感觉到前期的仔细规划有多么重要。这次也是回到了基本的C++语言来编写,在查找参考他人的代码时,会碰到一些以前从未使用过的C++11标准与STL库等,通过对这些知识的学习,感觉自己对于C++的理解与掌握又更深了一步。
5.结对编程的体会:
这次的结对编程是两人一起完成一个程序,在编写代码的过程中,一个人充当驾驶员,另一个人充当领航员,在这种模式下,当一方出现代码编写的bug时,另一方就会及时提醒,使其及时更正,与个人编程相比,结对编程一定程度上减少了很多不必要的bug的后期调试时间,当一方较为疲累是,也能相互鼓励监督,提高了编程效率。
结对编程有利有弊,在一定程度上他是限制了写代码的自由,因为你需要考虑你的搭档的情况,但他又有着比个人编程更为优势的一点是你能接触到不同的思路,可以开拓自己的思维。总而言之,只要双方掌握好交流的度,互相尊重,结对编程能达到事半功倍的效果。
6.3项在编码、争论等活动中花费时间较长,给我较大收获的事件
1)初期解题思路发生分歧
起初我的思路是先解决计算表达式问题,再解决随机生成表达式问题,而我的搭档则恰好相反,两相商讨下,我们最后采用了先计算再生成的思路。由于解题思路商定费了一定的时间,加之一人查阅相关资料,另一人编写代码,刚开始双方步调都不太能协调的上,所以在编写代码前就费时不少。
2)编写代码时功能2的实现
功能2加入了小数和小括号计算,与之对应的题目生成就也必须加上小数和小括号的随机生成,这就与功能1相比上升了不少难度。我们两人在编码时就不知道该如何去解决这个问题,两人在网上查找资料,互相讨论,总结资料上的思路,最后是通过随机生成的2个整数相除来生成整数与小数,小括号则是利用对称保证它一定是成对出现的。
3)编写代码时功能3的实现
功能3需要判断表达式不重复,以及控制台结果输出到文本。后者还较为简单,但前者就比较复杂,讨论时我们一开始想着判断数字是否相等,以及数字间的运算符是否相等,但发现这种方法判断起来耗时速度慢,操作也比较复杂,查阅相关资料,参考其他同学的思路,发现只要判断数字是否重复即可,于是将数字存储到set集里,利用sort函数排序在比较是否有重复,以实现这一功能。
7.照片