四则运算03 结对开发

Posted 伟哥8882

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了四则运算03 结对开发相关的知识,希望对你有一定的参考价值。

要求如下:

  1. 能够判断用户输入的答案是否正确。
  2. 能够处理四种运算的混合运算。连续的减法和除法,应遵守左结合的规定;连续除法要打括号,否则引起歧义。

代码如下:

1 #include <iostream>
  2 #include <iomanip>
  3 #include <fstream>
  4 #include <ctime>
  5 #include <cstdlib>
  6 #include <sstream>
  7 #include <string>
  8 #include <vector>
  9 #include <algorithm>
 10 #include <stack>
 11 #include <cctype>
 12 #include <cmath>
 13 using namespace std;
 14 bool checkResult(string exp,string result);
 15 string pri[7][7]={
 16     {">",">","<","<","<",">",">"},
 17     {">",">","<","<","<",">",">"},
 18     {">",">",">",">","<",">",">"},
 19     {">",">",">",">","<",">",">"},
 20     {"<","<","<","<","<","=",""},
 21     {">",">",">",">","",">",">"},
 22     {"<","<","<","<","<","","="}
 23 };
 24 
 25 bool isTrueFraction(int numerator, int denominator)    //判断产生的分数是否是真分数
 26 {
 27     if(numerator >= denominator)
 28         return false;
 29 
 30     for(int i = 2 ; i <= numerator ; i++)      //判断分数是否能够约分
 31     {
 32         if(numerator % i ==0 && denominator % i == 0)
 33             return false;
 34     }
 35 
 36     return true;
 37 }
 38 
 39 string getNum(int start = 0, int end = 100, bool isParentheses = false, int depth = 0)
 40 {
 41     int n = rand();
 42     if(isParentheses)      // 若带括号 则为 a op ( b op c ) 的形式 op为运算符
 43     {
 44         int num1 = rand() % (end - start + 1) + start;
 45         stringstream ss;
 46         ss << num1;
 47 
 48         if(depth  < 9)     //控制递归层数,带括号的式子少于10个
 49         {
 50             string num2 = "( " + getNum(start, end, n % 2 == 0,depth + 1) + " )";
 51             return ss.str() +" , "+ num2;
 52         }
 53         else
 54         {
 55             string num2 = "( " + getNum(start, end, false) + " )";
 56             return ss.str() +" , "+ num2;
 57         }
 58     }else
 59     {
 60         if(n % 7 == 0)       //若随机数n是7的倍数,产生一个真分数和一个整数,否则为两个整数
 61         {
 62             int num1 = rand() % (end - start + 1) + start;
 63             int num2 = rand() % (end - start + 1) + start;
 64             int num3 = rand() % (end - start + 1) + start;
 65 
 66             if(isTrueFraction(num1,num2))
 67             {
 68                 stringstream s1,s2,s3;   //将int转为string
 69                 s1<<num1;
 70                 s2<<num2;
 71                 s3<<num3;
 72                 return s1.str()+"/"+s2.str()+" , "+s3.str();
 73             }else
 74             {
 75                 return getNum(start,end);
 76             }
 77         }else
 78         {
 79             int num1 = rand() % (end - start + 1) + start;
 80             int num2 = rand() % (end - start + 1) + start;
 81             stringstream s1,s2;
 82             //string ss1,ss2;
 83             s1<<num1;
 84             s2<<num2;
 85             return s1.str()+" , "+s2.str();
 86         }
 87     }
 88 
 89 
 90 }
 91 
 92 char getOperator(string num2 = "1", bool isMulandDiv = true)   // 默认第二个参数不为0,默认包括乘除法
 93 {
 94     char op[] = {‘+‘,‘-‘,‘*‘,‘/‘};
 95 
 96     if(isMulandDiv)
 97     {
 98         if(num2 == "0") //避免除数为0
 99             return op[rand() % 3];
100         else
101             return op[rand() % 4];
102     }else
103     {
104         return op[rand() % 2];     //只包含加减
105     }
106 }
107 
108 bool isDup(vector<string> &items, string item)    //若重复返回true,否则返回false
109 {
110     if(find(items.begin(),items.end(),item) == items.end())
111         return false;
112     else
113         return true;
114 }
115 
116 bool isNegative(string num1, string num2, char op)      //判断两数加减的正负
117 {
118     stringstream ss1,ss2;
119     int n1,n2;
120     ss1 << num1;
121     ss1 >> n1;
122     ss2 << num2;
123     ss2 >> n2;
124     if(op == ‘-‘)
125     {
126         if(n1 < n2)
127         {
128             return true;
129         }else
130         {
131             return false;
132         }
133     }else
134     {
135         if(n1 + n2 < 0)
136             return true;
137         else
138             return false;
139     }
140 
141 }
142 
143 bool isRemainder(string num1, string num2)   //判断两数相除有无余数
144 {
145     stringstream ss1,ss2;
146     int n1,n2;
147     ss1 << num1;
148     ss1 >> n1;
149     ss2 << num2;
150     ss2 >> n2;
151 
152     if(n1 % n2 == 0)
153         return false;
154     else
155         return true;
156 }
157 
158 void printAndCheck(vector<string> &items, bool isCmd)
159 {
160     vector<string>::iterator it = items.begin();
161     string answer;
162     if(isCmd)  //答案是分数或整数
163     {
164 
165         for(;it != items.end(); it++)
166         {
167             cout << (*it) <<endl;
168             cout << "answer:";
169             cin >> answer;
170             if(checkResult((*it),answer))
171             {
172                 cout << "Bingo!!!" <<endl;
173             }else
174             {
175                 cout << "Oh ,stupid guy!!" <<endl;
176             }
177         }
178 
179         cout << "Problems finished!" << endl;
180     }else
181     {
182         ofstream of("problems.txt");
183         if(!of)
184             exit(1);
185 
186         for(;it != items.end() ; it++)
187             of<<*it <<endl;
188 
189         of.close();
190     }
191 
192 
193 }
194 int getOpersNum(string op)    //返回运算符编号,用于比较优先级 +-*/()# 对应 0123456
195 {
196     if(op == "+")
197     {
198         return 0;
199     }else if(op == "-")
200     {
201         return 1;
202     }else if(op == "*")
203     {
204         return 2;
205     }else if(op == "/")
206     {
207         return 3;
208     }else if(op == "(")
209     {
210         return 4;
211     }else if(op == ")")
212     {
213         return 5;
214     }else if(op == "#")
215     {
216         return 6;
217     }else
218     {
219         cout << "Error!" << endl;
220         exit(1);
221     }
222 }
223 
224 double calculate(double n1, double n2, string op)
225 {
226     if(op == "+")
227     {
228         return n1 + n2;
229     }else if(op == "-")
230     {
231         return n1 - n2;
232     }else if(op == "*")
233     {
234         return n1 * n2;
235     }else if(op == "/")
236     {
237         return n1 / n2;
238     }else
239     {
240         cout << "Error" <<endl;
241         exit(1);
242     }
243 }
244 
245 bool checkResult(string exp,string result)
246 {
247     stack<string> ops;
248     stack<double> nums;
249     ops.push("#");
250     exp = exp + " #";    //加起始结束标识
251     int flag = -1;  //字符串扫描标识 标识" "的位置
252     while (!( exp[flag + 1] == ‘#‘ && ops.top() == "#"))  //当符号栈栈顶元素和扫描出的下一个元素不同时为#时
253     {
254         string str = exp.substr(flag + 1, exp.find(" ",flag + 1) - flag -1);
255         if(str != "+" && str != "-" && str != "*" && str != "/" && str != ")" && str != "(" && str != "#")    //不是运算符
256         {
257             if(str.find("/") == string::npos)   //str为整数的情况
258             {
259                 stringstream ss;
260                 double n;
261                 ss << str;
262                 ss >> n;
263                 nums.push(n);
264                 flag = exp.find(" ",flag + 1);
265             }else    //分数的情况
266             {
267                 stringstream ssNum,ssDen;
268                 double numerator,denominator;     //分子 分母
269                 ssNum << str.substr(0,str.find("/"));
270                 ssDen << str.substr(str.find("/") + 1);
271                 ssNum >> numerator;
272                 ssDen >> denominator;
273                 nums.push(numerator/denominator);  //以double型压栈
274                 flag = exp.find(" ",flag + 1);
275             }
276         }else   //str是运算符
277         {
278             if(pri[getOpersNum(ops.top())][getOpersNum(str)] == "<")
279             {
280                 ops.push(str);
281                 flag = exp.find(" ",flag + 1);
282             }else if(pri[getOpersNum(ops.top())][getOpersNum(str)] == ">")
283             {
284                 string op = ops.top();
285                 ops.pop();
286                 double num2 = nums.top();
287                 nums.pop();
288                 double num1 = nums.top();
289                 nums.pop();
290                 nums.push(calculate(num1,num2,op));
291             }else if (pri[getOpersNum(ops.top())][getOpersNum(str)] == "=")
292             {
293                 ops.pop();
294                 flag = exp.find(" ",flag + 1);
295             }else
296             {
297                 cout << "Error!!!";
298                 exit(1);
299             }
300         }
301     }
302     double res;
303     if(result.find("/") == string::npos)  //结果是整数
304     {
305         stringstream ss;
306         ss << result;
307         ss >>res;
308     }else  //结果是分数
309     {
310         stringstream ssNum,ssDen;
311         double numerator,denominator;     //分子 分母
312         ssNum << result.substr(0,result.find("/"));
313         ssDen << result.substr(result.find("/") + 1);
314         ssNum >> numerator;
315         ssDen >> denominator;
316         res = numerator/denominator;
317     }
318 
319     if(fabs(nums.top() - res) < 1e-4)    //int,double,string多次转换可能结果有偏差,设置10的-4次方为阈值检验结果正确性
320     {
321         return true;
322     }else{
323         return false;
324     }
325 }
326 
327 int main(int argc, char *argv[])
328 {
329     srand((int)time(0));    //设定时间种子
330     vector<string> items;   //将题目存在items中,用于判断是否重复和输出
331     int itemNum,tmp;  //题目数量
332     char ttmp;
333     bool isCmd;    //打印方式
334     bool isMulandDiv;     //是否有乘除法
335     bool isParentheses;  //是否带括号
336     int start,end;  //数值范围
337     bool isNeg;    //有无负数
338     bool isRem;    //有无余数
339     bool addFlag = false;  //添加标识
340 
341     cout << "Please input the problem number:" << endl;       //定制题目数量、打印方式等
342     cin >> itemNum;
343     if(itemNum < 0 )
344     {
345         cout << "Error input!" <<endl;
346         exit(1);
347     }
348 
349     cout << "Please input the display mode(0. screen 1.file)" <<endl;
350     cin >> tmp;
351     if(tmp == 0)
352     {
353         isCmd = true;
354     }else if(tmp == 1)
355     {
356         isCmd = false;
357     }else
358     {
359         cout << "Error input!" <<endl;
360         exit(1);
361     }
362 
363     cout << "Include multiplication or division? (Y/N)" <<endl;
364     cin >>ttmp;
365     if(ttmp == ‘y‘ || ttmp == ‘Y‘)
366     {
367         isMulandDiv = true;
368     }else if (ttmp == ‘N‘ || ttmp == ‘n‘)
369     {
370         isMulandDiv = false;
371     }else
372     {
373         cout << "Error!"<<endl;
374         exit(1);
375     }
376 
377     cout << "Include parenthesses? (Y/N)" <<endl;
378     cin >>ttmp;
379     if(ttmp == ‘y‘ || ttmp == ‘Y‘)
380     {
381         isParentheses = true;
382     }else if (ttmp == ‘N‘ || ttmp == ‘n‘)
383     {
384         isParentheses = false;
385     }else
386     {
387         cout << "Error input!"<<endl;
388         exit(1);
389     }
390 
391     cout << "Please input the number range:(devide by a blank)" << endl;
392     cin >> start >> end ;
393     if(start > end)
394     {
395         swap(start, end);
396     }
397 
398     cout << "Is additions and subtractions have negative result?(Y/N)" <<endl;
399     cin >> ttmp;
400     if(ttmp == ‘y‘ || ttmp == ‘Y‘)
401     {
402         isNeg = true;
403     }else if (ttmp == ‘N‘ || ttmp == ‘n‘)
404     {
405         isNeg = false;
406     }else
407     {
408         cout << "Error input!"<<endl;
409         exit(1);
410     }
411 
412     cout << "Is divisions have remainder?(Y/N)" <<endl;
413     cin >> ttmp;
414     if(ttmp == ‘y‘ || ttmp == ‘Y‘)
415     {
416         isRem = true;
417     }else if (ttmp == ‘N‘ || ttmp == ‘n‘)
418     {
419         isRem = false;
420     }else
421     {
422         cout << "Error input!"<<endl;
423         exit(1);
424     }
425 
426     for(;items.size() != itemNum ;)    //根据条件生成问题
427     {
428         string num = getNum(start,end,isParentheses);
429         while (num.find(",") != string::npos)
430         {
431             addFlag = true;
432             if( num[num.find(",") + 2] == ‘(‘)     //运算符后紧跟括号,运算符选取只和isMulandDiv有关
433             {
434                 char op = getOperator("1",isMulandDiv);
435                 stringstream ss;
436                 ss << op;
437                 num = num.replace(num.find(","),1,ss.str());
438             }else                             //运算符后是数字,运算符选取和num2和isMulandDiv有关,此时是不带括号或最右边的算式
439             {
440                 string num2 = num.substr(num.find_last_of(",") + 2, num.find_first_of(")") - num.find_last_of(",") - 3);   //error at num.find(")",num.find(",") + 2) - 1
441                 char op = getOperator(num2,isMulandDiv);
442                 stringstream ss;
443                 ss << op;
444                 num = num.replace(num.find(","),1,ss.str());
445                 int begin = 0;    //找到形如 a op b 的式子
446                 if(num.find("(") != string::npos)   //如果式子里有()的话
447                     begin = num.find_last_of("(") + 2;
448                 string num1 = num.substr(begin,num.find(ss.str()) - 1);
449                 //num2 = num.substr(num.find_last_of(ss.str()) + 2,num.find_first_of(")") - 1);
450                 if(op == ‘-‘ || op == ‘+‘)
451                 {
452 
453                     if(!isNeg && isNegative(num1,num2,op))
454                     {
455                         addFlag = false;
456                         break;
457                     }
458 
459                 }else if(op == ‘/‘)
460                 {
461                     if(!isRem && isRemainder(num1,num2))
462                     {
463                         addFlag = false;
464                         break;
465                     }
466                 }
467             }
468 
469         }
470         if(!addFlag)    //满足要求,可以添加
471         {
472             continue;
473         }
474 
475         if(!isDup(items,num))    //判断是否重复,不重复则添加
476         {
477             items.push_back(num);
478         }
479     }
480     printAndCheck(items,isCmd);
481 
482     return 0;
483 }
由于之前是自主研究 现在变为结对开发所以我们对一份代码进行了认真的修改,以实现更多的功能 ,生成的数字和运算符之间都由空格分隔,以便于计算结果。主要添加的是bool checkResult(string exp,string result) 函数,
改函数的核心就是数据结构课本上栈的应用---表达式计算。所以我们进行了,对以前栈的知识的重新学习,便于使用
运算符的优先级关系表如下:
  + - * / ( ) #
+ > > < < < > >
- > > < < < > >
* > > > > < > >
/ > > > > < > >
( < < < < < =  
) > > > >   > >
# < < < < <   =

 

checkResult(string exp,string result) 中表达式是以string传入
通过一个扫描标识位flag记录游标移动到的位置,
  1. 建立并初始化符号栈ops和数值栈nums,将表达式起始符 # 压入符号栈ops。
  2. 由扫描标识位flag扫描获取表达式中的数值或运算符,循环执行3~5直至求出整个表达式的值。
  3. 取出ops的栈顶元素,当ops的栈顶元素和当前扫描到的符号均为 # 时,整个表达式求值完成,这时nums的栈顶元素为表达式的值。
  4. 若取出的字符串不是运算符,则压入nums,并继续扫描。
  5. 若取出的字符串是运算符,则根据ops栈顶元素和运算符优先级比较结果,做不同处理。

    ①若 <,则运算符压入ops栈,并继续扫描。

    ②若 >,则弹出ops的栈顶运算符,从nums弹出两个数(此处弹出顺序注意),进行相应计算,结果压入nums栈。

    ③若 =,则ops的站定元素是"("且运算符是")",这时弹出ops的栈顶元素"(",相当于去掉括号,然后继续扫描。

 结果的判断是用两个double类型变量进行比较。如果两者的差的绝对值小于10-4 ,则认为两数相等。

以下是运行截图:

技术分享

技术分享

 

以上是关于四则运算03 结对开发的主要内容,如果未能解决你的问题,请参考以下文章

四则运算3(结对开发)

201571030314《小学生四则运算练习软件》结对项目

结对项目:四则运算题目生成器(Java)

结对编程项目四则运算

结对编程-单元测试

四则运算结对作业