• 四则运算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 ,则认为两数相等。

    以下是运行截图:

  • 相关阅读:
    Linux下动态库(.so)和静态库(.a) 的区别
    CTS、CLS、CLR
    ASP.NET简介及网页基础知识
    ASP.NET MVC中ActionResult的不同返回方式
    ADO.NET中的数据库帮助类
    ASP.NET MVC 方法View返回的两种方式
    使用win10 IIS 发布局域网网站
    ASP.NET MVC 给Action的参数赋值的方式
    ASP.NET MVC简单流程解释(传值方式)
    ASP.NET MVC 简介(附VS2019和VSCode版示例)
  • 原文地址:https://www.cnblogs.com/liuwei8882/p/5296169.html
Copyright © 2020-2023  润新知