后缀表达式实战:Qt制作计算器

导言

相信学过数据结构的人都听说过后缀表达式,就是在学习栈的时候。可能也有很多人实现过这一算法,不过基本上也都是在控制台窗口里用用。相信大家也都用过计算器windows里面的calc。但是有没发现它只能单步计算,而不能一次计算一个表达式。后缀表达式就有了用武之地,可以一次性计算一整个个式子。科技要为生产服务,所以我就实际去做了一个依据后缀表达式的带有图形化界面的计算器。

什么是后缀表达式

后缀表达式又称逆波兰式,用于简化计算数学表达式,是计算器类软件开发的重要理论依据。这部分有两个要点:

  • 中缀表达式转后缀表达式
  • 后缀表达式的计算

中缀表达式转后缀表达式

正常的数学表达式就是一个中缀表达式,假设使用字符串存储这个表达式。设置一个运算符栈op,并把字符‘\0‘进栈,再设置一个保存结果的字符串(字符数组)suffix。然后顺序扫描中缀表达式,如果是数字字符则直接加入到suffix,如果是 运算符 + - * /。则和运算符栈op的栈顶元素比较,若是比栈顶运算符的级别高,则进栈。否则退出栈顶元素,把它加入到suffix,然后重复上一操作继续与栈顶比较,直到比栈顶级别高然后进栈为止。

其中符号的优先级:

# * 、/ +、-
0 0 1 2

其中左括号较为特殊,如果是左括号则直接进入op,无需比较。进栈之后左括号用于最低优先级,可以理解为这是一个新的数学表达式的开始。若是右括号,则运算符栈一直退栈,直到退出一个左括号为止。在扫描完中缀表达式后,把op栈中的元素依次出栈加到suffix中(除了#)。注意每个数字之后要加空格符分隔,以免两个相邻的数字产生歧义。

后缀表达式的计算

设置一个新的栈S存储double类型的数据。从左到右扫描suffix字符串,遇到空格符,就把它前面的数字字符都转换成相应的double型数字,然后压入栈S中。遇到操作符就要把栈S连续出栈两次,然后结合该操作符进行相应的计算,再把结果压入栈S中。直到扫描完suffix字符串,那么栈S的栈顶元素即为结果。

理论说了这么多,相信大家一定很难只看文字就能体会的。所以接下来我们进入实战部分

Qt实战

关于图形化界面的绘制这部分不是本文重点,故不做介绍。最简单的方式就是新建Qt的ui文件,然后用拖控件的方式绘制出来(囧)。基本的思路就是:

  1. 按一个按键,比如数字键,运算符(+ - * /)或者左右括号,然后可以在计算器屏幕上显示出来。
  2. 当你按下等于号 = 的时候,开始计算。
  3. 从计算器显示屏(使用QLneEdit实现)获取字符串QString。
  4. 把获取的中缀串转化成后缀串
  5. 从后缀串计算出结果,并让计算器显示屏显示出来。

实现中缀转成后缀

我们的主窗口类是MainWindow,在其中添加一public函数:toPostfix()  在这个函数中我们首先要获得已经输入的表达式字符串。

QString exp = ui->lineEdit->text();

这里的lineEdit就是我们计算器的显示屏对象指针。它的text方法返回它所显示的文字。然后为了比较不同运算符的优先级,我们要再定义一个函数 getLevel

int MainWindow::getLevel(const QChar &oper)
{
    switch(oper.cell())
    {
    case ‘#‘:
    case ‘(‘:return 0;
    case ‘+‘:
    case ‘-‘:return 1;
    case ‘*‘:
    case ‘/‘:return 2;
    }
    return 0;
}

注意QChar类型当然是不能直接放到switch里面的。它的cell方法返回普通char类型。我们还要在MainWindow类里定义两个私有成员:

  • 一个栈来opStack充当运算符栈。QStack qt的栈类型,为了保持一致性以及避免一些问题。我们最好不要采用C++的STL里的Stack。幸运的是他们的方法名很多都是一样的。
  • 一个字符串postfix用来接受转换后的后缀表达式。QString类型。

最后toPostfix函数为:

void MainWindow::toPostfix()
{
    QString exp = ui->lineEdit->text();

    for(int i=0;i<exp.length();i++)
    {
        if(exp[i].isDigit()||exp[i]==‘.‘)
        {
            postfix.push_back(exp[i]);
        }
        else if(exp[i]==‘(‘)
        {
            opStack.push(exp[i]);
        }
        else if(exp[i]==‘)‘)
        {
            postfix.push_back(‘ ‘);//空格用于分隔
            while(opStack.top()!=‘(‘)
            {
                postfix.push_back(opStack.pop());
            }
            opStack.pop();
        }
        else if(getLevel(exp[i])>getLevel(opStack.top()))
        {
            postfix.push_back(‘ ‘);
            opStack.push(exp[i]);
        }
        else
        {
            postfix.push_back(‘ ‘);qDebug()<<postfix;
            while(getLevel(exp[i])<=getLevel(opStack.top()))
                postfix.push_back(opStack.pop());
            opStack.push(exp[i]);
        }
    }
    while(opStack.top()!=‘#‘)
    {
        QChar c = opStack.pop();
        postfix.push_back(‘ ‘);
        postfix.push_back(c);
    }
}

实现后缀表达式计算

定义一个函数evaluation用于计算。我们要新建一个double类型的栈来保存每一次运算的结果。一个临时字符串用于存储后缀表达式中的数字。

QString tem;
QStack<double> ans;

QString类有方便的方法来进行字符串与基本数据类型之间的转换。比如

tem.toDouble()

此外,因为字符串也是一种容器,所以它支持clear方法来清空字符串。就能获得一个double类型的数据啦。是不是很方便。最后evaluation函数为:

void MainWindow::evaluation()
{
    QString tem;
    QStack<double> ans;
    for(int i=0;i<postfix.size();i++)
    {
        if(postfix[i].isDigit()||postfix[i]==‘.‘)
            tem.push_back(postfix[i]);
        else if(postfix[i]==‘ ‘)
        {
            if(!tem.isEmpty())
            {
                ans.push(tem.toDouble());
                tem.clear();//转换完了就清空。
            }
        }
        else
        {
            double a,b;
            a=ans.pop();
            b=ans.pop();
            switch(postfix[i].cell())
            {
            case ‘+‘:ans.push(b+a);break;
            case ‘-‘:ans.push(b-a);break;//注意a,b顺序
            case ‘*‘:ans.push(b*a);break;
            case ‘/‘:ans.push(b/a);break;//注意a,b顺序
            }
        }

    }
    //计算器屏幕显示结果
    ui->lineEdit->setText(QString::number(ans.top()));
}

项目地址

时间: 2024-10-29 01:09:03

后缀表达式实战:Qt制作计算器的相关文章

栈实现综合计算器(中缀表达式),前缀,中缀,后缀表达式,逆波兰计算器

思路: 代码:实现多位数的运算 public class Calculator { public static void main(String[] args) { //根据前面老师思路,完成表达式的运算 String expression = "7*2*2-5+1-5+3-4"; // 15//如何处理多位数的问题? //创建两个栈,数栈,一个符号栈 ArrayStack2 numStack = new ArrayStack2(10); ArrayStack2 operStack =

QT之计算器对四则运算表达式的解析

前面我们已经关于计算器介绍的已经够多了,那么它现在还是没有具备计算的功能. 今天我们来继续讲解计算器的解析算法,那么对于一个四则运算表达式, 它是如何读懂的呢?比如:"+9.11 + ( -3 - 1 ) * -5 ": 人类习惯的数学表达式叫做中缀表达式,还有一种将运算符放在数字后面的后缀表达式, 比如:5 + 3 ==> 5 3 +: 1 + 2 * 3 ==> 1 2 3 * +;像这种就是后缀表达式. 那么中缀表达式是符合人类的阅读和思维习惯,后缀表达式则符合计算机

深入浅出数据结构C语言版(8)——后缀表达式、栈与四则运算计算器

在深入浅出数据结构(7)的末尾,我们提到了栈可以用于实现计算器,并且我们给出了存储表达式的数据结构(结构体及该结构体组成的数组),如下: //SIZE用于多个场合,如栈的大小.表达式数组的大小 #define SIZE 1000 //表达式的单个元素所使用的结构体 typedef struct elem { int num = 0; //若元素存储操作数则num为该操作数 char oper = '='; //若元素存储操作符则oper为该操作符 bool IsNum = false; //用于

使用qt制作一个简单的计算器

前言:今天使用qt制作了一个很简单的计算器,觉得挺有意思的,所以在这里跟大家分享一下. 这里先跟大家说说使用到的函数: 一.槽连接函数 connect(信号发送者,发送的信号,信号接收者,信号接收者的槽函数) //前面我有一篇文章已经介绍过槽函数的使用了,大家有兴趣可以看看,这里就不详细说了.  二.取出按钮中的字符 QString line = ui->pushButton1->text() //取出按钮pushButton1中的字符存放到line中 三.设置输入框中显示的内容 ui->

前缀、中缀、后缀表达式以及简单计算器的实现

前缀表达式(波兰表达式).中缀表达式.后缀表达式(逆波兰表达式) 介绍 三种表达式都是四则运算的表达方式,用以四则运算表达式求值,即数学表达式的求解. 前缀表达式 前缀表达式是一种没有括号的算术表达式,与中缀表达式不同的是,其将运算符写在前面,操作数写在后面.为纪念其发明者波兰数学家Jan Lukasiewicz,前缀表达式也称为“波兰式”.例如,- 1 + 2 3,它等价于1-(2+3). 中缀表达式 中缀表达式就是一般的算数表达式,操作符以中缀形式出现在操作数之间. 后缀表达式 后缀表达式指

后缀表达式(逆波兰表达式)计算器

package datastructure.stack; import java.util.*; /** * <h3>netty_lecture</h3> * <p>逆波兰计算器</p> * 1+((2+3)*4)-5 ==> 1 2 3 + 4 * + 5 1 * @author : myron * @date : 2020-03-18 23:48 **/ public class PolandNotation { private static fi

使用后缀表达式写的数据结构实验,实现计算器

数据结构实验需要使用后缀表达式进行计算的设计 自己写的可以实现简单的‘+-*/’运算以及包括‘() [] {} 指数 小数 2e3’等的运算,用于交作业,功能很少,代码如下 #include <stdio.h> #include <stdlib.h> #include <malloc.h> #include <stdbool.h> #include <math.h> //定义操作数的最大位数 #define MAX 64 typedef stru

后缀表达式做计算器程序

概念: 后缀表达式是相较于中缀表达式而言的,像我们平时写的2+3*(4-(5+6))/7就是一个中缀表达式,那么如何将之变为后缀表达式呢?后缀表达式如何用来求解呢? 先来第一个问题(中缀->后缀): 变为后缀表达式方法(规则): 1.遇到操作数:直接添加到后缀表达式中 2.栈为空时,遇到运算符,直接入栈 3.遇到左括号:将其入栈 4.遇到右括号:执行出栈操作,并将出栈的元素输出,直到弹出栈的是左括号,左括号不输出. 5.遇到其他运算符:加减乘除:弹出所有优先级大于或者等于该运算符的栈顶元素,然后

计算器核心算法——中缀表达式转为后缀表达式

中缀表达式转后缀表达式的过程类似编译过程——四则运算表达式中的括号必须匹配——根据运算符优先级进行转换——转换后的表达式中没有括号——转换后可以顺序的计算出最终结果 这是某位伟人研究出的算法,在这里我们直接拿来用就可以. 转换过程:——当前元素e为数字:输出——当前元素e为运算符:1.与栈顶运算符进行优先级比较2.小于等于:将栈顶元素输出,转13.大于:将当前元素e入栈 ——当前元素e为左括号:入栈——当前元素e为右括号:1.弹出栈顶元素并输出,直至栈顶元素为左括号2.将栈顶的左括号从栈中弹出