目 录
1 问题描述 3
2 需求分析 3
2.1 数据需求 3
2.2 功能需求 3
3 概要设计 3
3.1 主体设计 3
3.2 用户界面设计 4
3.3抽象数据类型 4
3.3.1 科学计算器 4
3.3.2 多项式计算器 5
3.3.3 历史记录 6
3.4 功能模块设计 6
3.4.1 科学计算器 6
3.4.2多项式计算器: 7
3.4.3.历史记录 9
4 详细设计及系统实现 9
4.1 科学计算器 9
4.1.1 科学计算器数据结构 9
4.1.2 科学计算器界面 10
4.1.3 科学计算机算法实现 15
4.2多项式计算器 21
4.2.1 多项式计算器数据结构 21
4.2.2 多项式计算器界面 22
4.2.3多项式计算器算法实现 23
4.3 历史记录 29
4.3.1 历史记录数据结构 29
4.3.2 历史记录界面 29
4.4 程序打包 30
5 系统调试分析 34
6 课程设计总结 38
6.1实现功能 38
6.2有待改进部分 39
6.3自我总结 39
附录: 39
3.4 功能模块设计
3.4.1 科学计算器
1.确定科学计算器按键及其功能.
2.设置基本运算符对应的元数(Qmap实现)
3.当输入”=”后,通过正则表达式,将输入中的运算符和操作数区分开,并将划分好的子串临时储存在向量中.
4.从向量起始位置到末尾,依次判断元素.若该元素为操作数,入操作数栈;若该元素为运算符时,则判断其能否进运算符栈.判断规则如下:
(1)运算符栈底压入”#”.
(2) 若运算符包含”(”,直接进栈
(3) 若运算符不为”)”且不包含”(”,首先判断该运算符是否为”-”.若是,判断该减号位置.若其位于表达式起始处或者向量中减号前一位置元素包含”(”,在操作数栈中压入0.此后判断该运算符与运算符栈顶符号的优先级.若该运算符优先级大于栈顶元素,该运算符入栈;否则栈顶元素出栈并进行对应操作:若为一元运算符,操作数栈出栈一个数,处理后进操作数栈;若为二元运算符,操作数栈出栈两个数,处理后进操作数栈.
(4)若运算符为”)”,运算符栈和操作数栈持续执行(2)中对应操作,直至扫描到一个运算符包含”(”,该运算符再出栈执行(2)对应操作.
(5)扫描到”#”,若表达式正确,则代表算式计算完成,操作数栈栈顶元素即为所求.
5.将操作数栈顶元素出栈,写入输出框,同时将整个算式写入历史记录中.
6. 计算过程中,要对部分可能出现异常进行处理:
(1)括号需匹配
(2)log,ln函数的底数部分需大于0且不等于1,指数部分需大于0
(3)偶次幂根号下数字需大于0
(4)除数不能为0
(5)任何数的0次幂为1
7.字母表达式(运算符)都以”(”结尾,其运算也同括号规律相同.
8.向量实现减号取负方式:
若减号为初始位or减号前一位存在括号
操作数栈压入0
此后减号继续当作二元运算符使用即可
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include"sub_window.h"
#include"history_recording.h"
#include"ui_history_recording.h"
#include"QtMath"
#include"QRegularExpression"
#include"QRegularExpressionMatch"
#include"QRegularExpressionMatchIterator"
#include"iterator"
#include"QDebug"
#include"QVector"
//需要匹配的式子与操作数:
QString s=R"(floor\(|\~\(|[+\-*\/()=%]|ceil\(|log\(|logbase\(|ln\(|\^\(|yroot\(|fact\(|abs\(|cuberoot\(|sqrt\(|sqr\(|cube\(|sin\(|cos\(|tan\(|sec\(|csc\(|cot\(|[1-9][0-9]*\.?[0-9]*|0|Π|\be\b)";//用于入队列
QString qs_1=R"(floor\(|\~\(|[+\-*\/()=%]|ceil\(|log\(|logbase\(|ln\(|\^\(|yroot\(|fact\(|abs\(|cuberoot\(|sqrt\(|sqr\(|cube\(|sin\(|cos\(|tan\(|sec\(|csc\(|cot\()";//用于入运算符栈
QString qs_2=R"([1-9][0-9]*\.?[0-9]*|0|Π|\be\b)"; //用于入操作数栈
QString qs_4=R"(\w*\()"; //括号匹配
//括号匹配(仅匹配左小括号!在计算前先匹配下)
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
setWindowTitle("Calculator");
//固定窗口大小
this->setMinimumSize(this->size());
this->setMaximumSize(this->size());
setStyleSheet( //设置按钮样式
"QPushButton {"
"border-radius: 12px;"
"border: 1px solid #000000;"
"color: #000000;"
"}"
"QPushButton:hover{"
"border-radius: 12px;"
"border: 1px solid #000000; "
"background-color: qlineargradient(spread:pad, x1:0, x2:0, y1:0, y2:1,"
" stop: 0 rgba(200,220,240,255),"
" stop: 0.504 rgba(173,216,230,255),"
" stop: 0.505 rgba(171,215,225,255),"
"stop: 1 rgba(200,220,240,255));"
"}"
"QPushButton:pressed{"
" border-radius: 12px;"
"background-color: qlineargradient(spread:pad, x1:0, x2:0, y1:0, y2:1,"
" stop: 0 rgba(200,220,240,255),"
" stop: 0.504 rgba(230,230,230,255),"
" stop: 0.505 rgba(230,230,230,255),"
"stop: 1 rgba(200,220,225,255));"
" border: 1px solid #000000; "
"}"
);
//此代码修改了扩展窗口的背景色,使得其可以对初始页面进行覆盖
QPalette p;
ui->Tri_Show_GUI->setAutoFillBackground(true);
p.setBrush(ui->Tri_Show_GUI->backgroundRole(),QBrush(QColor(255,255,255)));
ui->Tri_Show_GUI->setHidden(true);
ui->Tri_Show_GUI->setPalette(p);
ui->f_Func_GUI->setAutoFillBackground(true);
p.setBrush(ui->f_Func_GUI->backgroundRole(),QBrush(QColor(255,255,255)));
ui->f_Func_GUI->setHidden(true);
ui->f_Func_GUI->setPalette(p);
//历史记录设置背景
ui->History_Rec->setStyleSheet(
"border-image:url(D:/qtico/history_rec.png)");
ui->shizi_TextArea->setAlignment(Qt::AlignRight);
QFont font =QFont("楷体",16);
ui->shizi_TextArea->setFont(font);
ui->jieguo_TextArea->setAlignment(Qt::AlignRight|Qt::AlignBottom);
hr->ui->textBrowser->setAlignment(Qt::AlignRight);
hr->ui->textBrowser->setFont(font);
InitOpPrior();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::Calculate(){
Operator_Vector.swap(*new QVector<QString>);
Operator_Stack.clear();
Operator_Stack.push("#");
OperaNum_Stack.clear();
QRegularExpression re(s);
QRegularExpression re_1(qs_2);
QRegularExpression re_4(qs_4);
QString tem;
tem=ui->shizi_TextArea->toPlainText();
QRegularExpressionMatchIterator qem=re.globalMatch(tem); //将操作数与运算符分隔开,入队列
QRegularExpressionMatch match ;
while(qem.hasNext()){
match=qem.next();
tem=match.captured(0);
Operator_Vector.append(tem);
}
//开始处理数据!!!!!!
re.setPattern(qs_1);
QVector<QString>::iterator it=Operator_Vector.begin();
while(it!=Operator_Vector.end()){ //
if(ui->jieguo_TextArea->text()=="Error")
return;
tem=*it;
if(re_1.match(tem).hasMatch()) //如果数字匹配
{
if(tem=="e")
OperaNum_Stack.push(2.71828182845904523536);
else if(tem=="Π")
OperaNum_Stack.push(3.14159265358979323846);
else
OperaNum_Stack.push(tem.toDouble());
}
else if(re.match(tem).hasMatch()){ //如果运算符匹配
if(tem==")"){ //dealpart到一个运算符包含"("为止.
while(!re_4.match(Operator_Stack.top()).hasMatch()){
Deal_Parly();
}
Deal_Parly();
}
else if(re_4.match(tem).hasMatch()){
Operator_Stack.push(tem);
}
else if(tem=="="){ //匹配到等号
while(!Operator_Stack.isEmpty()) //当运算符没有匹配到最后
Deal_Parly();
}
else //基本运算符
{
if(tem=="-"){ //如果是减号
//如果是第一项是减号,什么也不做,下面考虑了,此处考虑减号位于括号后!且存在*(it-1)
if(it==Operator_Vector.begin()){
OperaNum_Stack.push(0);
}
else if(re_4.match(*(it-1)).hasMatch())//减号前一项带括号了,减号要当作负号对待!且该减号是一定可以入栈的!
OperaNum_Stack.push(0); //在操作数栈中压一个0
}
QString qs_top=Operator_Stack.top();
if(re_4.match(qs_top).hasMatch())
Operator_Stack.push(tem);
else if(Get_Prior(tem)>Get_Prior(qs_top))
Operator_Stack.push(tem);
else{
Deal_Parly();
Operator_Stack.push(tem);
}
}
}
it++;
}