四则运算03 结对开发
要求如下:
- 能够判断用户输入的答案是否正确。
- 能够处理四种运算的混合运算。连续的减法和除法,应遵守左结合的规定;连续除法要打括号,否则引起歧义。
代码如下:
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记录游标移动到的位置,
- 建立并初始化符号栈ops和数值栈nums,将表达式起始符 # 压入符号栈ops。
- 由扫描标识位flag扫描获取表达式中的数值或运算符,循环执行3~5直至求出整个表达式的值。
- 取出ops的栈顶元素,当ops的栈顶元素和当前扫描到的符号均为 # 时,整个表达式求值完成,这时nums的栈顶元素为表达式的值。
- 若取出的字符串不是运算符,则压入nums,并继续扫描。
- 若取出的字符串是运算符,则根据ops栈顶元素和运算符优先级比较结果,做不同处理。
①若 <,则运算符压入ops栈,并继续扫描。
②若 >,则弹出ops的栈顶运算符,从nums弹出两个数(此处弹出顺序注意),进行相应计算,结果压入nums栈。
③若 =,则ops的站定元素是"("且运算符是")",这时弹出ops的栈顶元素"(",相当于去掉括号,然后继续扫描。
结果的判断是用两个double类型变量进行比较。如果两者的差的绝对值小于10-4 ,则认为两数相等。
以下是运行截图:


浙公网安备 33010602011771号