计算器(有qt界面)

目录

计算逻辑

思路

确定优先级 

代码

中缀转后缀 

为什么要用栈?

括号的处理

代码

结果计算 

代码

界面

思路

清除

回退

=


计算逻辑

思路

  • 可以考虑将算术表达式先转为后缀,再用后缀进行结果的计算
  • 也可以直接在算术表达式中求值

(但因为老师的要求,要用中缀转后缀的方法,所以这里就只介绍第一种)

(悄咪咪)其实第二种我也没研究过,不知道难不难(跪)

确定优先级 

因为中缀转后缀是需要按照优先级先后来确定对运算符号的处理的,所以,首先先把优先级确定好

这里我用的是map,数字越高,优先级越高

  • 这样可以通过传入的符号,不用判断,可以直接拿到它对应的优先级
  • (当然同一优先级中还是得一个一个判断)
代码
void Widget::confirm_priority(map<string, int>& comp)
{
    comp.insert(make_pair("=", -1));

    comp.insert(make_pair("||", 1));

    comp.insert(make_pair("&&", 2));

    comp.insert(make_pair("==", 3));
    comp.insert(make_pair("!=", 3));

    comp.insert(make_pair(">", 4));
    comp.insert(make_pair("<", 4));
    comp.insert(make_pair("<=", 4));
    comp.insert(make_pair(">=", 4));

    comp.insert(make_pair("+", 5));
    comp.insert(make_pair("-", 5));

    comp.insert(make_pair("*", 6));
    comp.insert(make_pair("/", 6));
    comp.insert(make_pair("%", 6));

    comp.insert(make_pair("!", 7));
}

中缀转后缀 

这个在之前栈里提到过,但只是写了思路,当时没有写代码

基本上就是这么个思路,遇到同/低优先级的,就开始出栈中存放的运算符号,存进后缀表达式中

除此之外,还要注意,因为要实现的运算还挺多的,里面就有两个字符的运算符号,需要特殊处理

为什么要用栈?

我个人感觉是为了方便进行括号匹配

  • 因为括号是从左到右的,如果是栈,就可以从右到左完成括号内的遍历
  • 但要是队列?似乎不太对劲
  • 如果有多个括号,那就会让外层的括号先出去,这样不符合运算逻辑了
  • 但也可以用队列,只不过要倒腾一下
括号的处理
  • 左括号不用管
  • 遇到右括号就开始处理括号里面的符号
  • (可以把括号内的表达式看作一个独立的中缀,遇到右括号就相当于遍历完成,所以要把栈中符号出完)

代码
vector<string> Widget::to_suffix(string arr, map<string, int>& comp,string& bug)
{
    string numbers("0123456789");
    vector<string> ans; //存放后缀
    stack<string> cal;//存放符号
    for (auto i = 0; i < arr.size(); ++i)
    {

        if (arr[i] >= '0' && arr[i] <= '9')
        {
            auto pos = arr.find_first_not_of(numbers, i);
            string tmp_arr(arr.begin() + i, arr.begin() + pos);
            ans.push_back(tmp_arr);
            i = pos - 1;  //因为这次用掉的是数字,pos位置不是数字, 且for要i++
        }
        else
        { // 符号
            if (arr[i]=='-'&&arr[i + 1] >= '0' && arr[i + 1] <= '9') { //处理负数
                string tmp;tmp += arr[i];
                auto pos = arr.find_first_not_of(numbers, i+1); //i+1是为了跳过那个负号

                string tmp_arr(arr.begin() + i+1, arr.begin() + pos);
                ans.push_back(tmp+tmp_arr); //负号也要添加进去
                i = pos-1;
                continue;
            }
            string sym;
            sym += arr[i];
            if (((arr[i] == '>' || arr[i] == '<' || arr[i] == '=' || arr[i] == '!') && arr[i + 1] == '=')||(arr[i] == '|'&&arr[i + 1] == '|')|| (arr[i] == '&' && arr[i + 1] == '&'))  //有些符号是两个字符,需要处理
            {
                ++i;
                sym += arr[i];
            }

            if (sym == "(") //左括号直接进cal,不需要其他步骤
            {
                cal.push(sym);
                continue;
            }
            else if (sym == ")") //右括号就需要开始处理了
            {
                while (cal.top() != "(")
                {
                    ans.push_back(cal.top());//把括号内的符号直接进(因为是符合规则的)
                    cal.pop();
                }
                cal.pop(); //记得把左括号出掉
            }
            else
            {
                while (!cal.empty())
                {
                    string top = cal.top();
                    if (comp[sym] > comp[top])
                    {
                        break;
                    }
                    else
                    {
                        ans.push_back(top);
                        cal.pop();
                    }
                }
                cal.push(sym);//栈为空,直接进
            }
        }
    }
    while (!cal.empty())  //将剩余的符号直接插入ans
    {
        ans.push_back(cal.top());
        cal.pop();
    }
    return ans;
}

结果计算 

就着后缀计算就很简单了,优先级已经被排好了,只需要遇到符号就取栈顶两个数字(或者一个,!是单目运算符)

还有就是要处理一下负数

代码
int Widget::work(vector<string>& tokens, map<string, int>& comp)
{
    std::stack<int> s;
    int num = 0;
    for (auto c : tokens)
    {
        if (c == "=") {
            break;
        }
        if (comp[c] != 0) // 是符号
        {
            int ans = 0;
            int ret = comp[c];

            if (comp[c] != 7)
            {
                // 拿到运算数据
                int a = s.top();
                s.pop(); // 先拿到的是右
                int b = s.top();
                s.pop(); // 然后是左

                if (ret == 1)
                {
                    ans = b || a;
                }
                else if (ret == 2)
                {
                    ans = b && a;
                }
                else if (ret == 3)
                {
                    if (c == "==")
                    {
                        ans = b == a;
                    }
                    else
                    {
                        ans = b != a;
                    }
                }
                else if (ret == 4)
                {
                    if (c == ">")
                    {
                        ans = b > a;
                    }
                    else if (c == ">=")
                    {
                        ans = b >= a;
                    }
                    else if (c == "<")
                    {
                        ans = b < a;
                    }
                    else
                    {
                        ans = b <= a;
                    }
                }
                else if (ret == 5)
                {
                    if (c == "+")
                    {
                        ans = b + a;
                    }
                    else
                    {
                        ans = b - a;
                    }
                }
                else if (ret == 6)
                {
                    if (c == "*")
                    {
                        ans = b * a;
                    }
                    else if (c == "/")
                    {
                        if(a!=0)
                        ans = b / a;
                        else{
                            ui->lineEdit_2->insert("除数不能为0");
                            return (size_t)-1;
                        }
                    }
                    else
                    {
                        ans = b % a;
                    }
                }
            }
            else
            {
                // 拿到运算数据
                int a = s.top();
                s.pop(); // 单目

                ans = !a;
            }
            s.push(ans); // 结果入栈
        }
        else
        {
            // 是数字 -- 要string转int,然后数字入栈
            if (c[0] == '-') {
                c.erase(0,1);
                num = stoi(c);
                num = -num;
            }
            else {
                num = stoi(c);
            }
            s.push(num);
        }
    }
    return s.top(); // 最后一个元素就是结果
}

界面

思路

qt我没系统学过,就是看了一下别人写的代码,然后照猫画虎写出来

感觉挺简单的,界面是直接做好的,你只需要在界面后面加上一点需要的代码就行

比如:我在界面上按了数字1:

void Widget::on_pushButton_clicked()
{
    ui->lineEdit->insert("1");
    Q += '1';
}

它里面是要定义一个qstring类型的变量,用来保存输入的字符

按了1之后,就在输入框里显示1,然后把"1"插入到Q里面就行

其他字符也是一样的逻辑

清除

void Widget::on_pushButton_23_clicked()
{
    ui->lineEdit_2->clear();//清空输出框
}

回退


void Widget::on_pushButton_12_clicked()
{
    ui->lineEdit->backspace();//删除lineEdit中一个元素
    Q=Q.left(Q.length()-1);
}

=

void Widget::on_pushButton_22_clicked()
{
    ui->lineEdit->insert("=");
    if(Q.size() == 0)
    {
        ui->lineEdit_2->insert("你还未输入数字,无法计算");//异常处理,提醒用户输入错误
    }

    else
    {
        Q += '=';
        char *s = Q.toLocal8Bit().data();//QString 转 char*;
        map<string, int> comp;
        confirm_priority(comp);
        string arr,bug;
        arr+=s;

        vector<string> ret = to_suffix(arr, comp,bug);
        //ui->lineEdit_2->insert(bug.c_str());

        int ans=work(ret, comp);
        if(ans==(size_t)-1){
            Q="";
        }
        else{
            QString str = QString::number(ans,10);//int 转化成 QString
            ui->lineEdit_2->insert(str);
            Q="";
        }
    }
}

(里面那个bug变量是我用来调试的,因为我不会在qt里面调试(呜呜呜))

=的时候,就说明表达式已经输入完毕,就要开始进行计算逻辑了

最后把结果插入到计算器的输出框即可

完整的代码已经上传到资源绑定那里了,有需要的自己拿就行

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值