第三次作业——四则运算

要求0:

作业地址:https://edu.cnblogs.com/campus/nenu/2016CS/homework/2266

要求1:

git仓库地址:https://git.coding.net/wangc556/f4.git

 要求2:

结对同学博客地址:https://www.cnblogs.com/wangcwcgnaw/p/9926468.html 

结对同学名字:王储 2016011925

解题思路:

根据王储同学的提示,四则运算求值一定要用到逆波兰表达式,所以我百度搜了一下逆波兰表达式的作用和用法。

将一个普通的中序表达式转换为逆波兰表达式的一般算法是:

(1)首先构造一个运算符栈,此运算符在栈内遵循越往栈顶优先级越高的原则。

(2)读入一个用中缀表示的简单算术表达式,为方便起见,设该简单算术表达式的右端多加上了优先级最低的特殊符号“#”。

(3)从左至右扫描该算术表达式,从第一个字符开始判断,如果该字符是数字,则分析到该数字串的结束并将该数字串直接输出。

(4)如果不是数字,该字符则是运算符,此时需比较优先关系。

 做法如下:将该字符与运算符栈顶的运算符的优先关系相比较。如果,该字符优先关系高于此运算符栈顶的运算符,则将该运算符入栈。倘若不是的话,则将栈顶的运算符从栈中弹出,直到栈顶运算符的优先级低于当前运算符,将该字符入栈。

(5)重复上述操作(3)-(4)直至扫描完整个简单算术表达式,确定所有字符都得到正确处理,我们便可以将中缀式表示的简单算术表达式转化为逆波兰表示的简单算术表达式。

之后的每个功能在使用逆波兰表达式的基础上又做了一些细节的改动。

每个功能重、难点:

功能一:实现运算数为整数的不含小括号的四则运算,运算符具有优先级,对优先级进行规定。

功能二:在功能一的基础上支持了小括号,括号参与运算后是考虑括号的匹配和生成顺序,定义temp为未匹配的左括号数来解决括号匹配问题。同时考虑运算数据为浮点型,判断结果为无限小数还是有限小数很困难。

功能三:生成的四则运算表达式是不重复的并且工整输出。因为表达式重复的情况太多所以我认为这是一大难点,也是我们不太知道如何解决的。

1.进行题目数、功能、文件路径等变量定义

int main(int argc,char* argv[]){
    string ss;//题目数的字符串
    int qwq=0;//功能
    string path;//输出的文件路径
    for(int i=1;i<argc;++i){
        string str=argv[i];
        if(str=="-n"||str=="-c"){
            ss=argv[i+1];
            if(str=="-n") qwq=1;
            else qwq=2;
        }
        if(str=="-f") qwq=3,path=argv[i+1];
    }
    int num=0,l,cnt=0;//num问题数,cnt回答正确数
    //把问题数字符串转化为整数
    l=ss.size();
    for(int i=0;i<l;++i){
        num*=10;
        num+=ss[i]-'0';
    }
    //每次生成的随机数都不同
    srand((int)time(0));

 2.生成运算式,使用rand()函数,功能一随机生成1-10的整数,功能二、三生成0.01-10.00的浮点数

string s="";//生成的运算式的字符串
        int temp=0;//未匹配的左括号数
        //生成运算式
        if(qwq==1){
            for(int i=1;i<=4;++i){
                if(i==1){
                    int r1=rand2(1,10);
                    //整数转字符串
                    stringstream ssss;
                    ssss<<r1;
                    string t;
                    ssss>>t;
                    s+=t;
                }
                else{
                    int r1=rand2(1,5);
                    if(r1==1) s+="+";
                    if(r1==2) s+="-";
                    if(r1==3) s+="*";
                    if(r1==4) s+="/";
                    int r2=rand2(1,10);
                    stringstream ssss;
                    ssss<<r2;
                    string t;
                    ssss>>t;
                    s+=t;
                }
            }
        }

  定义temp为当前位置未被匹配的左括号数量,在之后的位置以temp为上界,将未匹配的的括号进行匹配,保证括号合法。

else{
            for(int i=1;i<=5;++i){
                if(i==1){
                    int r1=rand2(0,2);
                    for(int i=0;i<r1;++i) s+="(";
                    temp+=r1;//temp:未匹配的左括号数
                    double r2=rand1(1,1000);
                    stringstream ssss;
                    ssss<<r2;
                    string t;
                    ssss>>t;
                    s+=t;
                }
                else if(i==5){
                    for(int i=0;i<temp;++i) s+=")";
                    temp=0;
                }
                else{
                    int r1;
                    //生成右括号
                    if(temp){
                        r1=rand2(0,temp);
                        for(int i=0;i<r1;++i) s+=")";
                        temp-=r1;
                    }
                    int r2=rand2(1,5);
                    if(r2==1) s+="+";
                    if(r2==2) s+="-";
                    if(r2==3) s+="*";
                    if(r2==4) s+="/";
                    //生成左括号
                    r1=rand2(0,2);
                    for(int i=0;i<r1;++i) s+="(";
                    temp+=r1;
                    double r3=rand1(1,1000);
                    stringstream ssss;
                    ssss<<r3;
                    string t;
                    ssss>>t;
                    s+=t;
                }
            }
        }

3.定义两个栈运算符栈和数字栈 应用逆波兰表达式

stack<double>sv;
        stack<char>sp;
        char c;
        int k=0,flag=1;
        double x,y;
        sp.push('\0');
//逆波兰表达式实现
        c=s[k];
        while(flag){
            if(c>='0'&&c<='9'||c=='.') sv.push(to_num(s,k));
            else if(c=='\0'&&sp.top()=='\0') flag=0;
            else if(c=='('||(level(c)>level(sp.top()))){
                sp.push(c);
                k++;
            }
            else if(c==')'&&sp.top()=='('){
                sp.pop();
                k++;
            }
            else if(level(c)<=level(sp.top())){
                x=sv.top();
                sv.pop();
                if(sv.empty()) y=0;
                else{
                    y=sv.top();
                    sv.pop();
                }
                c=sp.top();
                sp.pop();
                switch(c){
                    case '+':y = x+y; break;
                    case '-':y = y-x; break;
                    case '*':y = x*y; break;
                    case '/':y = y/x; break;
                }
                sv.push(y);
            }
            c=s[k];
        }
        double ans=sv.top();//运算式的值

4.在功能三上我们不太知道如何避免重复,所以采用map映射来存储结果判断结果是否出现过,以保证表达式不同

//四舍五入
        int dd=ans*10000;
        if(dd%10>=5) dd=dd-dd%10+10;
        else dd=dd-dd%10;
        ans=dd/10000.0;
        dd/=10000.0;
        if(mp[ans]){
            j--;
            continue;
        }
        else mp[ans]++;

编程体会: 

    由于自己的编程能力不强,所以我找了编程能力较强的王储同学作为队友,希望可以在结对编程的过程中学习到一些知识,提高自己。谢谢王储同学对我提出的疑问耐心解答,对我不会的知识点进行讲解,我觉得自己在这个过程中学到了很多有了一些提高。

    我在结对编程之前读了《构建之法》第四章两人合作这一部分知识,邹欣老师说,在结对编程中,任何一段代码都至少被两双眼睛看过,被两个脑袋思考过,结对编程避免了“我的代码”还是“他的代码”的问题,使而整个代码的责任不属于某个人,而是属于两个人,进而属于整个团队。还有结对编程中驾驶员和领航员的角色要经常互换,避免长时间紧张工作而导致的观察力和判断力下降。所以我认为结对编程还是很有必要的,对于一个好性能的软件或者程序,我感觉结对编程是不可缺少的。

3项在编码、争论等活动中花费时间较长,给你较大收获的事件:

(1)定义两个栈 使用逆波兰表达式

(2)括号匹配 判断数据是否有限/无限循环并四舍五入保留三位小数

(3)如何判断重复的表达式并不再计算重复的式子

收获:在此过程中学到了很多编程思想,在队友的讲解下感觉方法很巧妙。

猜你喜欢

转载自www.cnblogs.com/yangmx-97/p/9925212.html