day-1
今天主要做了3件事。1、了解了实现算数表达式求值底层逻辑的三种方法:(a)用栈和队列分别存储运算符和操作数;(b)双栈算符优先级法 (c)用二叉树求解后缀表达式的值。个人感觉第二种方法思路比较好,操作性强。2、安装了QT(虽然我不知道怎么用),用QT来实现计算器的界面设计。3、从b站看了相关的QT课程,为下一步实现第一个属于自己的作品做好铺垫。
今天看的内容仅仅了解一些计算方法的思路和使用QT的基本知识。明天还得再看看相关视屏,争取开始写实现算数表达式的代码
day-2
今天开始了计算器的界面设计,完成了基本按钮的设置,结果如图所示
相关的C++头文件
#ifndef WIDGET_H
#define WIDGET_H
enum BtnType{
Num,//数字
Op,//操作符
Dot,//点
Back,//退格
Equl,//等于
Clear//清除
};
#include
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::Widget *ui;
QString num1;//操作数1
QString num2;//操作数2
QString op;//操作符
};
#endif // WIDGET_H
用枚举对按钮进行说明。没有“( )”操作符是因为这是我初步对计算器的设计,后面还要添加相应的模块
主要收获:
学会使用QT中的pushbutton
个人感觉还是很好用的,首先将需要使用的按钮创建出来,然后调节按钮的大小。
下面就是对按钮的精细调节
接下来就是要实现按钮的功能,在头文件中定义按钮。用枚举的方式实现按钮的分类。
具体的算数运算明天实现,后续内容明天更新,,,,,
day-03
今天实现了简单的四则运算,结果如下:
#include "Widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//数字按钮绑定
connect(ui->Num0,&QPushButton::clicked,[this](){onclicked(Num,"0");});
connect(ui->Num1,&QPushButton::clicked,[this](){onclicked(Num,"1");});
connect(ui->Num2,&QPushButton::clicked,[this](){onclicked(Num,"2");});
connect(ui->Num3,&QPushButton::clicked,[this](){onclicked(Num,"3");});
connect(ui->Num4,&QPushButton::clicked,[this](){onclicked(Num,"4");});
connect(ui->Num5,&QPushButton::clicked,[this](){onclicked(Num,"5");});
connect(ui->Num6,&QPushButton::clicked,[this](){onclicked(Num,"6");});
connect(ui->Num7,&QPushButton::clicked,[this](){onclicked(Num,"7");});
connect(ui->Num8,&QPushButton::clicked,[this](){onclicked(Num,"8");});
connect(ui->Num9,&QPushButton::clicked,[this](){onclicked(Num,"9");});
//运算符按钮绑定
connect(ui->Sum,&QPushButton::clicked,[this](){onclicked(Op,"+");});
connect(ui->Sub,&QPushButton::clicked,[this](){onclicked(Op,"-");});
connect(ui->Mul,&QPushButton::clicked,[this](){onclicked(Op,"*");});
connect(ui->Div,&QPushButton::clicked,[this](){onclicked(Op,"/");});
//其他
connect(ui->Back,&QPushButton::clicked,[this](){onclicked(Back,"back");});
connect(ui->Clear,&QPushButton::clicked,[this](){onclicked(Clear,"clear");});
connect(ui->Dot,&QPushButton::clicked,[this](){onclicked(Dot,".");});
connect(ui->Equ,&QPushButton::clicked,[this](){onclicked(Equ,"=");});
}
Widget::~Widget()
{
delete ui;
}
void Widget::onclicked(BtnType _type,QString _btn){
switch(_type){ //按钮类型
case Num:
{
if(mOp.isEmpty())//如果操作数为空,则为操作数一
{
mNum1+=_btn;
}
else //如果操作数不空,则为操作数二
{
mNum2+=_btn;
}
break;
}
case Op://运算符
{
mOp=_btn;
break;
}
case Dot:
{
if(mOp.isEmpty())
{
//操作数一的点
if(!mNum1.isEmpty() && !mNum1.contains("."))//操作数1非空且不包含"."
{
mNum1+=_btn;
}
}
else
{
if(!mNum2.isEmpty() && !mNum2.contains("."))//操作数2非空且不包含"."
{
mNum2+=_btn;
}
}
break;
}
case Back:
{ if(!mNum1.isEmpty()&&!mOp.isEmpty()&&!mNum2.isEmpty())//删除操作数二
{
mNum2.chop(1);//尾部删除,删除指定字符个数
}
else if (!mNum1.isEmpty()&&!mOp.isEmpty())
{
mOp.chop(1);
}
else if (!mNum1.isEmpty()) {
mNum1.chop(1);
}
break;
}
case Clear://清除
{
mNum1.clear();
mNum2.clear();
mOp.clear();
}
case Equ://等于
{
if(mNum1.isEmpty()||mNum2.isEmpty()||mOp.isEmpty())
return;
double num1=mNum1.toDouble();//将字符串转化为小数
double num2=mNum2.toDouble();//将字符串转化为小数
double result=0.0;
if(mOp=="+")
{
result=num1+num2;
}
else if (mOp=="-")
{
result=num1-num2;
}
else if (mOp=="*")
{
result=num1*num2;
}
else if (mOp=="/")
{
if(num2==0.0){
ui->lineEdit->setText("除数不能为0");
return;
}
else
{
result=num1/num2;
}
}
ui->lineEdit->setText(QString::number(result));//QString::number 数字转字符串
mNum1.clear();
mNum2.clear();
mOp.clear();
return;
}
}
ui->lineEdit->setText(mNum1+mOp+mNum2);}
##这里暂时没有加入计算器扩号运算,明天会继续优化一下。
主要掌握了一些类的函数,和字符串转换函数。逻辑实现主要靠switch实现,结构比较清晰;在涉及到除法的时候需要分类。
然后主要就是对按钮进行了绑定,通过lineEdit实现输出
day 04
今天主要处理操作符的优先级问题,主要难点在于将中缀表达式正确的转化为后缀表达式,然后按照优先级出栈,再将结果入栈,最后出栈。
主要代码如下:
void Exp(const char *S,char OPS[],int &len)//将中缀表达式转变为后缀表达式
{
QStack<char> OPE;//符号栈
unsigned int i,j=0;
unsigned int tmp = strlen(S);
for (i = 0; i < tmp; i++)
{
switch (S[i])
{
case'+':
if(OPE.isEmpty())//栈为空
OPE.push(S[i]);
else if (OPE.top() == '*' || OPE.top() == '/')
{
OPS[j++] = OPE.pop();//弹出比'+'运算符优先级高和相等的运算符,依次加入后缀表达式
i--;
}
else
OPE.push(S[i]);
break;
case'-':
if(i!=0 && '('!=S[i-1])//正数
{
if(OPE.isEmpty())//栈为空
OPE.push(S[i]);
else if (OPE.top() == '*' || OPE.top() == '/')//弹出比'-'运算符优先级高和相等的运算符,依次加入后缀表达式
{
OPS[j++] = OPE.pop();
i--;
}
else
OPE.push(S[i]);
}
else//负数
{
while ((S[i] >= '0'&&S[i] <= '9' ) || S[i] == '.' || ('-'==S[i]&&(S[i-1]<'0'||S[i-1]>'9')))
{
OPS[j++] = S[i];
if('-'==S[i])
OPS[j++]='@';
i++;
}
i--;
OPS[j++] = '#'; //数字中的间隔符
}
break;
case'*':
if(OPE.isEmpty())//栈为空
OPE.push(S[i]);
else
OPE.push(S[i]);
break;
case'/':
if(OPE.isEmpty())//栈为空
OPE.push(S[i]);
else
OPE.push(S[i]);
break;
case'(':
OPE.push(S[i]);
break;
case')':
while (OPE.top() != '(')//依次把栈中的运算符加入后缀表达式并将其出栈
{
OPS[j++] = OPE.pop();
}
OPE.pop();//从栈中弹出'('
break;
default:
while ((S[i] >= '0'&&S[i] <= '9') || S[i] == '.' || ('-'==S[i]&&S[i-1]<'0'&&S[i-1]>'9'))
{
OPS[j++] = S[i];
i++;
}
i--;
OPS[j++] = '#'; //数字中的间隔符
break;
}
}
while (!OPE.isEmpty())
{
OPS[j++] = OPE.pop();
}
len = j;
}
void PostExp(char B[], int len, double &result,bool &flag)//用后缀表达式计算结果
{
int i;
double a;
double b;
double c;
QStack<double>SZ;
for (i = 0; i < len; i++)
{
switch (B[i])
{
case'+':
{
a = SZ.pop();
b = SZ.pop();
c = b + a;
SZ.push(c);
}
break;
case'-':
{
if('@'!=B[i+1])
{
a = SZ.pop();
b = SZ.pop();
c = b - a;
SZ.push(c);
}
else
{
int jx = 0;
double dx;
char *stx = new char[10];
while (B[i] != '#')
{
if('@'!=B[i])
stx[jx++] = B[i];
i++;
}
dx = atof(stx);//把字符串转换成浮点数
SZ.push(dx);
delete stx;
}
}
break;
case'*':
{
a = SZ.pop();
b = SZ.pop();
c = b*a;
SZ.push(c);
}
break;
case'/':
{
a = SZ.pop();
b = SZ.pop();
if (a == 0)
{
flag = false;
return;
}
c = b / a;
SZ.push(c);
}
break;
default:
int j = 0;
double d;
char *st = new char[10];
while (B[i] != '#')
{
st[j++] = B[i];
i++;
}
d = atof(st);//把字符串转换成浮点数
SZ.push(d);
delete st;
break;
}
}
result=SZ.top();
}`
运行结果: