四则运算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 ,则认为两数相等。
以下是运行截图: