电子工程师技术服务社区
公告
登录
|
注册
首页
技术问答
厂商活动
正点原子
板卡试用
资源库
下载
文章
社区首页
文章
【野火i.MX6ULL ARM Linux开发板连载】轻小型计算器开发(二)
分 享
扫描二维码分享
【野火i.MX6ULL ARM Linux开发板连载】轻小型计算器开发(二)
野火
i.MX6ULL
Linux开发
瑟寒凌风
关注
发布时间: 2021-03-16
丨
阅读: 670
前面我们写了一个计算器,实现简单的加减乘除运算,但是我们发现它没有括号的功能,本节就要实现用括号来让我们的计算器功能变丰富。 在设计该功能时,我们先了解一下栈。 # 栈 栈是在程序设计中经常用到的一种非常重要的数据结构,是一种运算受限的线性表,其限制是仅允许在表的一端进行插入和删除运算。这一段称之为栈顶,相对的,把另一端称之为栈底。栈的特点便是先放入的东西总是后被拿出来,这个特性通常被称之为后进先出(LIFO)队列。栈这个数据结构定义了一些运算操作,两个最重要的是push和pop。push操作在栈的顶端加入一个元素。pop操作相反,在栈的顶端移除一个元素,并将栈的大小减一。干嘛非要规定先进后出呢?这样做有什么用呢?这是由于问题决定的。在解决一些计算问题的时候刚好就需要这样的一种后进先出的数据结构,然后便把它发明出来了,顺便取了个名字叫做栈。 # 前缀,中缀,后缀表达式 这三种表达式从根本上都是一种数学表达式。其中中缀表达式是最先出现的用来表述数学四则运算的表达式。而前缀表达式和后缀表达式则是一种和中缀表达式不同的,去除了运算优先级的表达式。 ## 中缀表达式 中缀表达式比较熟悉了如2*(3+4),这也是我们生活中常用的表达式,不过由于中缀表达式的规则比较复杂,所以计算机解决起来比较麻烦,所以计算器计算时,一般将其转换为前缀表达式或者后缀表达式再计算。 ## 前缀表达式 前缀表达式又称为波兰式,前缀表达式的运算符位于操作数之前,比如举一个最简单的+1 2,用中缀表达式表示为1+2。将中缀表达式2*(3+4)转换为前缀表达式则为* 2 + 3 4。中缀表达式的括号已经被去除掉了,这样就将计算操作变得简单了很多,计算机就可以只依靠出栈和进栈两种简单的操作完成前缀表达式的计算。计算机的求值方法为,从右到左扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对他们进行相应的计算,并将结果入栈;重复上述过程直到表达式最左端,最后运算得出的值即为表达式的结果。 ## 后缀表达式 后缀表达式又被称为逆波兰表达式,与前缀表达式相似,只是运算符位于操作符之后。比如1 2 +,用中缀表达式表达为1+2,看上去只是将前缀表达式倒过来了而已,事实确实如此,它和前缀表达式刚好对应,同样依靠出栈和进栈两种见到的操作就可以完成后缀表达式的全部运算。计算机的求值方法也差不多,从左到右扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们进行相应的计算,并将结果入栈;重复上述过程直到表达式最右端,最后运算得出的值即为表达式的结果。因为后缀表达式拥有和前缀表达式一样的特点,并且计算顺序更符合习惯,故一般使用较多。 ## 括号问题解决 中缀表达式因为计算机难以直接计算,故一般将其转换为前缀或后缀表达式再来进行计算。下面就来具体看一下如何将中缀表达式转换为后缀表达式。由于后缀表达式是没有运算符优先级和括号的,所以我们必须判断运算符的优先级,然后按照运算顺序将运算符排列。下面具体讲一个中缀转后缀的算法,首先创建一个代表后缀表达式的字符串和一个储存运算符的栈,然后从左到右扫描整个中缀表达式,如果扫描到数字则将数字添加到代表后缀表达式的字符串尾部,如果为运算符并且栈为空,将运算符压入栈中,如果栈不为空,比较栈顶元素和当前运算符的优先级,若当前运算符不大于栈顶元素的优先级,则将栈顶的运算符弹出加入后缀表达式的字符串中,并继续用当前运算符继续和栈顶运算符比较;否则将当前运算符也加入栈中。当中缀表达式扫描完,将栈中剩下的运算符依次弹出加入到后缀表达式的字符串尾部。这便是将中缀表达式转换为后缀表达式的整个过程,忽略了其中一些细节的地方,以免繁琐,不过其主要步骤都讲出来了。整个算法的核心便是利用栈来储存运算符,并且保持栈内的运算符的优先级总是递增的,再利用栈的后进先出的特性,恰好按照运算符的运算顺序将运算符添加到代表后缀表达式的字符串中。 在前面介绍后缀表达式的计算方法,从左到右扫描整个后缀表达式,遇到一个数就压到栈里,遇到一个运算符就从栈里面弹出两个数按运算符计算结果,然后将结果再压入到栈里,扫描完栈里面剩下的就是结果。 # 界面开发 打开之前的界面,对比如下图,在界面上添加如下控件 ![](https://cf03.ickimg.com/bbsimages/202103/d83f67e08ed30c9d0908ecd9d649fa6c.jpg) 双击widget.h文件,将文件中的私有成员改为 ```cpp private: //将表达式转化为后缀表达式 QString inToPost(QString infix) throw(const char*); //计算后缀表达式的结果 double compute(QString s) throw(const char*); void abortOperation(); Ui::Widget *ui; bool waitForOperand; QString error; ``` 转到widget.cpp文件中,添加几个要用到的头文件 ```cpp #include
#include
#include
``` 修改Widget构造函数的内容改为 ```cpp Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) { initUi(); waitForOperand = true; connectSlots(); setShortcutKeys(); } ``` 将on_clearBtn_clicked()的函数定义修改为 ```cpp void MainWindow::on_clearBtn_clicked() { //去掉末尾字符 QString str = ui->lineEdit->text(); if (str != "0") { ui->lineEdit->setText(str.left(str.count() - 1)); } } ``` 将digitClicked的函数修改为 ```cpp void Widget::digitClicked() { QPushButton *digitBtn = static_cast
(sender()); QString value = digitBtn->text(); if(ui->lineEdit->text() == "0" && value == "0") return; if(waitForOperand) { ui->lineEdit->setText(value); waitForOperand = false; } else { ui->lineEdit->setText(ui->lineEdit->text() + value); } } ``` 将on_equalBtn_clicked的函数修改为 ```cpp void Widget::on_equalBtn_clicked() { double result = 0.0; try { result = compute(inToPost(ui->lineEdit->text())); } catch(const char *er) { error = er; abortOperation(); return; } ui->lineEdit->setText(ui->lineEdit->text() + '=' + QString::number(result)); waitForOperand = true; } ``` 将on_sign_clicked的实现修改为 ```cpp void Widget::on_signBtn_clicked() { QString text = ui->lineEdit->text(); QChar sign = text[text.size() - 1]; if(sign == '-') { text.remove(text.size() - 1, 1); } else { text.append('-'); } ui->lineEdit->setText(text); } ``` 将operatorClicked的函数实现修改为 ```cpp void Widget::operatorClicked() { QPushButton *clickedBtn = qobject_cast
(sender()); QString clickedOperator = clickedBtn->text(); ui->lineEdit->setText(ui->lineEdit->text() + clickedOperator); } ``` 将pointClicked的函数实现修改为 ```cpp void Widget::on_pointBtn_clicked() { if (waitForOperand) ui->lineEdit->setText("0"); ui->lineEdit->setText(ui->lineEdit->text() + "."); waitForOperand = false; } ``` 将setShortCutKeys的函数实现更改为 ```cpp void Widget::setShortcutKeys() { Qt::Key key[21] = { Qt::Key_0, Qt::Key_1, Qt::Key_2, Qt::Key_3, Qt::Key_4, Qt::Key_5, Qt::Key_6, Qt::Key_7, Qt::Key_8, Qt::Key_9, Qt::Key_Plus, Qt::Key_Minus, Qt::Key_Asterisk, Qt::Key_Slash, Qt::Key_Enter, Qt::Key_Period, Qt::Key_Backspace, Qt::Key_M, Qt::Key_ParenLeft, Qt::Key_ParenRight, Qt::Key_AsciiCircum }; QPushButton *btn[21] = { ui->digitBtn0, ui->digitBtn1, ui->digitBtn2, ui->digitBtn3, ui->digitBtn4, ui->digitBtn5, ui->digitBtn6, ui->digitBtn7, ui->digitBtn8, ui->digitBtn9, ui->addBtn, ui->subtractionBtn, ui->multiplicationBtn, ui->divisionBtn, ui->equalBtn, ui->pointBtn, ui->clearBtn, ui->signBtn, ui->leftBracketBtn, ui->rightBracketBtn, ui->powBtn }; for (int i = 0; i < 21; i++) btn[i]->setShortcut(QKeySequence(key[i])); ui->clearAllBtn->setShortcut(QKeySequence("Ctrl+Backspace")); } ``` 修改connectSlots函数的实现 ```cpp QPushButton *digitBtns[10] = { ui->digitBtn0, ui->digitBtn1, ui->digitBtn2, ui->digitBtn3, ui->digitBtn4, ui->digitBtn5, ui->digitBtn6, ui->digitBtn7, ui->digitBtn8, ui->digitBtn9 }; for (auto btn : digitBtns) connect(btn, &QPushButton::clicked, this, &Widget::digitClicked); QPushButton *operatorBtns[7] = { ui->addBtn, ui->subtractionBtn, ui->multiplicationBtn, ui->divisionBtn, ui->leftBracketBtn, ui->rightBracketBtn, ui->powBtn }; for (auto btn : operatorBtns) connect(btn, &QPushButton::clicked, this, &Widget::operatorClicked); ``` 最后再添加刚才在私有成员里声明的的函数实现inToPost()和compute()函数。 在电脑上的运行效果如下图 ![](https://cf03.ickimg.com/bbsimages/202103/e91bc31eae759fd1d984654291c38910.jpg) 将源文件拷贝到ubuntu下,使用交叉编译工具让qt编译该源文件,可以编译出一个imx6ull能够运行的执行文件,通过nfs拷贝到开发板上,运行如下图 ![](https://cf03.ickimg.com/bbsimages/202103/0ae8061fbac32833d0ffac8e6bfea87b.jpg)
原创作品,未经权利人授权禁止转载。详情见
转载须知
。
举报文章
点赞
(
0
)
瑟寒凌风
关注
评论
(0)
登录后可评论,请
登录
或
注册
相关文章推荐
MK-米客方德推出工业级存储卡
Beetle ESP32 C3 蓝牙数据收发
Beetle ESP32 C3 wifi联网获取实时天气信息
开箱测评Beetle ESP32-C3 (RISC-V芯片)模块
正点原子数控电源DP100测评
DP100试用评测-----开箱+初体验
Beetle ESP32 C3环境搭建
【花雕体验】16 使用Beetle ESP32 C3控制8X32位WS2812硬屏之二
X
你的打赏是对原创作者最大的认可
请选择打赏IC币的数量,一经提交无法退回 !
100IC币
500IC币
1000IC币
自定义
IC币
确定
X
提交成功 ! 谢谢您的支持
返回
我要举报该内容理由
×
广告及垃圾信息
抄袭或未经授权
其它举报理由
请输入您举报的理由(50字以内)
取消
提交