利用Qt实现可视化科学计算器

📞个人信息  学号:102101433   姓名:林堂钦 

💡 作业基本信息  

【课程】福州大学2021级软件工程Ahttps://bbs.csdn.net/forums/ssynkqtd-05
作业要求链接https://bbs.csdn.net/topics/617294583
作业目标

实现一个简易计算器,要求具有图形化界面。

功能:具有基本功能的计算器

实现加、减、乘、除、归零基本操作。

附加功能:具有科学计算的计算器

实现次方、幂、三角函数等操作。

参考文献

1. https://www.cnblogs.com/xinz/archive/2011/11/20/2255830.html

2. https://www.cnblogs.com/xinz/archive/2011/10/22/2220872.html


目录

💡 作业基本信息  

📢 Gitcode项目地址

🔨 PSP表格

 💬 解题思路描述

📌 接口设计和实现过程

🔑 主要的类和函数接口展示

🔑 MainWindow类

🔑 CalObj类

🔑 CalTest类

🔑 bepexpression.h

🔑 function.h

✅ 关键功能展示

👓 四则运算基础功能实现

👓 科学运算功能展现

👓 复合运算功能展现

⏰性能改进与优化

🏳‍🌈 时间显示功能

🏳‍🌈 历史记录及清空操作

🏳‍🌈 帮助文档以及ui界面美化

💊 单元测试

🚩 异常处理措施与展示

🎵 心得体会


📢 Gitcode项目地址

玄澈_ / 阿钦的科学计算器 · GitCode


🔨 PSP表格

PSP是卡耐基梅隆大学(CMU)的专家们针对软件工程师所提出的一套模型:Personal Software Process (PSP, 个人开发流程,或称个体软件过程)。

PSP的目的是:记录工程师如何实现需求的效率,和我们使用项目管理工具(例如微软的Project Professional,或者禅道等)进行项目进度规划类似。

PSP2.1Personal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning计划1515
· Estimate· 估计这个任务需要多少时间1515
Development开发13701570
· Analysis· 需求分析 (包括学习新技术)540540
· Design Spec· 生成设计文档7070
· Design Review· 设计复审6040
· Coding Standard· 代码规范 (为目前的开发制定合适的规范)2030
· Design· 具体设计80180
· Coding· 具体编码300360
· Code Review· 代码复审6050
· Test· 测试(自我测试,修改代码,提交修改)240300
Reporting报告110115
· Test Repor· 测试报告6070
· Size Measurement· 计算工作量2015
· Postmortem & Process Improvement Plan· 事后总结, 并提出过程改进计划3030
· 合计14951700

 💬 解题思路描述

题目的要求是实现一个具有图形化界面的计数器,在实现普通的加减乘除四则运算的基础上,进一步实现科学计算器的相关内容,包括次方、幂、三角函数等相关操作。

出于作者的技术栈,本文选择了使用Qt跨平台C++开发工具在进行编写程序。

基于对题目的理解,以及查阅的相关知识,以下是本人实现该程序的一个解题思路

  1. 首先设计GUI界面。使用Qt Designer等来创建GUI界面,包括数字按钮、运算符按钮、科学函数按钮等,然后将其与代码和函数进行绑定。
  2. 实现数学运算等相关功能。使用Qt的信号和槽机制来实现这些功能。例如,当用户点击加号按钮时,我们需要通过实现相关的响应函数将两个数字相加将其回显到屏幕。

  3. 除了数学运算功能外,还需要实现科学计算功能。可以使用Qt Math库中的函数来实现这些功能。例如,当用户选择平方根函数时,我们需要计算输入数字的平方根并显示结果至屏幕。

  4. 在实现科学计算功能时,我们还需要考虑一些特殊情况。例如,当用户输入的数字为负数时,平方根函数将返回一个复数。我们需要处理这种情况并给出相应的提示信息

  5. 需要编写单元测试用例,确保每个功能都正常工作。我们可以使用Qt自带的测试框架QTest来编写单元测试用例。例如,我们可以测试加法、减法、乘法、除法以及相关的科学运算等功能的正确性。

  6. 通过编写的相关的函数来实现历史记录的功能,并在图形界面上附加当前时间的显示功能以及开发日志及提示等功能,使得该计算器的整体功能更加完善。


📌 接口设计和实现过程

🔑 主要的类和函数接口展示

在实现科学计算器的编程过程中,主要由以下几个类和函数接口类实现的。

  • MainWindow类 :MainWindow类是科学计算器程序中的一个主窗口类,它提供了一个应用程序的主窗口,其中包含了计算器的主要界面和相关按钮。MainWindow类同时会处理具体的用户操作并通过对应的反应函数做出相应的响应。
  • Dailog类:实现了开发日志和帮助文档的ui界面等。
  • CalObj类:实现了计算器的实例类,通过结合MainWindow类中的相关内容,实例化出具体的计算器类,实现了具体的计算函数和实现方法,同时也方便后续进一步的单元测试相关的内容。
  • CalTest类:通过编写该类,通过实例化CalObj类,能够调用QT自带的测试框架QTest来对该科学计算器进行单元测试,从而进一步提高程序的可靠性。
  • bepexpression.h:该头文件主要包含了检验表达式正确性,格式化表达式、通过逆波兰表达式等原理来进行计算器的计算等函数实现。
  • function.h:该头文件主要通过将内置的数学科学计算函数进行整合以及优化,进一步方便其他模块函数的调用和处理。

🔑 MainWindow类

上图是对MainWindow类的一个总体框架的分析,通过对整体的分析,可以将MainWindow类中的函数分为三类

  • 处理相关数字按钮信号(例如:输入1~9,π,e等)
  • 处理相关操作按钮信号(例如:输入+、-、*、÷等操作按钮)
  • 其他操作函数:例如更新时间、清除历史记录等

① 以on_button0_clicked()为例,展示处理相关数字按钮信号的关键代码:

void MainWindow::on_button0_clicked()
{ui->textDisplay->insert("0");
}

② 以on_buttonAdd_clicked()为例,展示简单四则运算操作的关键代码,该模块直接将相关的操作符载入缓存区中,为后续的计算做好准备:

void MainWindow::on_buttonAdd_clicked()
{ui->textDisplay->insert("+");
}

③ 以on_buttonLn_clicked()为例,展示科学运算过程中所需要的关键代码,参考Windows自带的科学计算器以及本项目的具体编写,本文进行这样的设定:计算器分为两种模式,分别为普通模式函数模式。且两种模式不能混用,即普通算式中不能出现函数,函数式中不能出现运算符这样的设定也符合系统自带计算器的相关操作。

在下面的函数模块中,我们通过一个flag标志来识别此时计算器处于哪一种计算模式,如果出现了计算模式的混用,则会对系统进行报错,并对用户进行提醒"此函数暂不支持混合运算!"

void MainWindow::on_buttonLn_clicked()
{if (flag != EMPTY){ui->statusbar->showMessage("此函数暂不支持混合运算!", 3000);return;}ui->textDisplay->insert("ln()");flag = LN;moveCursor();ui->textDisplay->setFocusPolicy(Qt::NoFocus);
}

④ on_buttonEqual_clicked(),这个类的重点函数实现,在这个函数中实现了对于表达式的计算操作,首先先判断计算模式是函数模式还是普通模式:如果是函数模式的话通过对于不同的科学操作函数进行不同的操作来得到最后返回的结果;如果是普通模式的话,通过bepexpression.h中的相关函数来对算术表达式进行检验,判断其是否合法,然后通过slove函数对其进行计算,最后得到返回结果并清空相关内容,以便下一次进行计算。

特点:在这个函数中,我们通过在类中定义一个 enum functionSituation 来对不同的计算操作进行标识,从而方便了后续代码的编写。同时,通过捕捉 errorCode 来针对不同的错误情况对用户进行提示,提高了软件的功能性

void MainWindow::on_buttonEqual_clicked()
{double answerNumber  = 0;QString qExpression  = ui->textDisplay->text();string stdExpression = qExpression.toStdString(); //转化为c++的stringif (flag != EMPTY) //函数模式{// enum functionSituation{EMPTY = 0,SQUARE,POWER,SQRT,LG,LN,FAC,SIN,COS,TAN,NUM};switch (flag){case SQUARE:{//将形似于x^2的表达式中的x提取出来QString qNumber = qExpression;qDebug() << qNumber << endl;qNumber.chop(2); // 去除尾部两个数字,留下所需数字double number = qNumber.toDouble();answerNumber = getPow(number, 2);break;}case POWER:{//将形似于x^(y)的表达式中的x提取出来int Length   = qExpression.length();int position = qExpression.indexOf("^");//分离出底数和指数QString qBaseNumber = qExpression;qBaseNumber.chop(Length - position);double baseNumber = qBaseNumber.toDouble();QString qPowNumber = qExpression.mid(position + 2);qPowNumber.chop(1);double powNumber = qPowNumber.toDouble();answerNumber = getPow(baseNumber, powNumber);break;}case SQRT:{//将形似于√(x)的表达式中的x提取出来QString qNumber = qExpression.mid(2);qNumber.chop(1);double number = solve(qNumber.toStdString());answerNumber = getSqrt(number);break;}case LG:{//将形似于lg(x)的表达式中的x提取出来QString qNumber = qExpression.mid(3);qNumber.chop(1);double number = solve(qNumber.toStdString());answerNumber = getLg(number);break;}case LN:{//将形似于ln(x)的表达式中的x提取出来QString qNumber = qExpression.mid(3);qNumber.chop(1);double number = solve(qNumber.toStdString());answerNumber = getLn(number);break;}case FAC:{//将形似于x!的表达式中的x提取出来QString qNumber = qExpression;qNumber.chop(1);double number = solve(qNumber.toStdString());answerNumber = getFac(number);break;}case SIN:{//将形似于sin(x)的表达式中的x提取出来QString qNumber = qExpression.mid(4);qNumber.chop(1);double number = solve(qNumber.toStdString());answerNumber = getSin(number);break;}case COS:{//将形似于cos(x)的表达式中的x提取出来QString qNumber = qExpression.mid(4);qNumber.chop(1);double number = solve(qNumber.toStdString());answerNumber  = getCos(number);break;}case TAN:{//将形似于tan(x)的表达式中的x提取出来QString qNumber = qExpression.mid(4);qNumber.chop(1);double number = solve(qNumber.toStdString());answerNumber  = getTan(number);break;}}QString answerString = QString::number(answerNumber, 'g', 6);//重置光标位置和标志,以进行下一次运算ui->textDisplay->setCursorPosition(ui->textDisplay->text().size());ui->textDisplay->insert("=" + answerString);flag = EMPTY;//记录历史信息ui->historyDisplay->append(ui->textDisplay->text());ui->statusbar->showMessage("记得Clear再进行下一次运算哦~", 3000);}else //普通算式模式{try{if (!analysis(stdExpression.c_str())) //尝试判断表达式是否合法{answerNumber = solve(stdExpression); // 用这个参数接返回的计算值QString answerString = QString::number(answerNumber, 'g', 6);//重置光标位置和标志,以进行下一次运算ui->textDisplay->setCursorPosition(ui->textDisplay->text().size());ui->textDisplay->insert("=" + answerString);flag = EMPTY;//记录历史信息ui->historyDisplay->append(ui->textDisplay->text());ui->statusbar->showMessage("记得Clear再进行下一次运算哦~", 3000);}}catch (int errorCode){switch (errorCode){case 1:ui->statusbar->showMessage("运算符位置不正确,请检查后重试",3000);break;case 2:ui->statusbar->showMessage("左右括号不匹配,请检查后重试", 3000);break;case 3:ui->statusbar->showMessage("存在不合法字符,请检查后重试", 3000);break;}}}
}

⑤ 相关的QT连接函数以及时间功能的实现

void MainWindow::timerUpdate()
{QDateTime time = QDateTime::currentDateTime();showtime->setText(time.toString("yyyy-MM-dd hh:mm:ss dddd"));
}void MainWindow::clearHistory()
{ui->historyDisplay->clear();
}void MainWindow::dealMenuBar()
{//连接按键退出槽connect(ui->actionExit, &QAction::triggered, this, &MainWindow::close);//连接历史记录清空槽connect(ui->actionClearHistory, &QAction::triggered, this,&MainWindow::clearHistory);//连接对话框打开槽connect(ui->actionAbout, &QAction::triggered, &myDialog, &QDialog::open);
}

🔑 CalObj类

    enum functionSituation{EMPTY = 0, //空SQUARE = 1,    //平方POWER = 2,     //幂SQRT = 3,      //平方根LG = 4,        // 10为底的对数LN,        // e为底的对数FAC,       //阶乘SIN,       //正弦COS,       //余弦TAN,       //正切NUM = 10       //普通数字计算};

CalObj类主要是用来对一个计算器进行实例化操作,方便后面进行单元测试。

其中的Func()函数也是通过实话化不同的计算器对象来得到最后的返回值,以便和测试用例的最终结果进行比较。


🔑 CalTest类

该类是在实例化CalObj类对象的基础上,利用Qt自带的QTest测试框架来对该项目进行单元测试。共编写了11个测试样例,分别针对平方、幂方、log、ln、阶乘等操作进行测试,具体的测试结果见下文所示。

部分测试函数的关键代码如下:

void CalTest::case1_squere()
{CalObj c("2^2", 1);//std::cout << c.Func() << std::endl;QVERIFY(c.Func() == 4);
}

🔑 bepexpression.h

这个模块主要实现了后台处理表达式的相关操作,包括以下几个函数:

  • int analysis(const char *expression) :检验算术表达式的合法性
  • string format(string str):处理负号开头的表达式,处理方法是在前面加个0
  • int comparePriorities(char c):比较运算符优先级
  • vector<string> Infix2Suffix(string str):中缀表达式转后缀
  • double result(vector<string> suffixExpression):计算后缀表达式结果
  • double solve(string str):调用前几个函数,相当于main函数,一步到位求解表达式

接下来介绍一下几个重要的函数,首先是int analysis(const char *expression),这个函数是从来检查算术表达式的合法性的,如果所检验的算术表达式是不合法的话,会抛出对应的异常,有如下几种异常异常代码: 1,运算符位置不正确;2,左右括号不匹配 ;3,存在不合法字符。

如果最后验证是一个正确的表达式的话,则会返回整数0

具体的关键函数实现如下:

int analysis(const char *expression)
{int opt = 0;int length = strlen(expression);bool isNumberEnd = false;int numberBracket = 0; //括号总数,检测到左括号就+1,检测到右括号就-1for (int i = 0; i < length;){if (expression[i] >= '0' && expression[i] <= '9'){if (isNumberEnd)throw 1;isNumberEnd = true; //此时检测到数字,所以置为真,因为下面要把连续的数字吃掉while ((expression[i] >= '0' && expression[i] <= '9') || expression[i] == '.')++i;}//数字吃掉了,轮到运算符char const *sop = "(+-*/)";opt = strchr(sop, expression[i++]) - sop; //计算运算符优先级,从左到右优先级依次增高if (opt < 0 || opt > 6)throw 3;if (opt == 0) //匹配到左括号{if (isNumberEnd) //此时括号数应为0,否则为错throw 1;numberBracket++;}if (opt == 5) //匹配到右括号{if (numberBracket <= 0) //如果是右括号,那么nbracket必大于0throw 2;numberBracket--;}if (opt > 0 && opt < 6 && !isNumberEnd) //运算符正确但后面没有数字throw 1;if (opt > 0 && opt < 5) //匹配到+-*和/,数字末端置falseisNumberEnd = false;}if (numberBracket) //括号不匹配throw 2;if (!isNumberEnd) //运算符缺少运算数throw 1;return 0;
}

接下来是vector<string> Infix2Suffix(string str),这个函数主要实现了中缀表达式转后缀的相关操作,大致流程如下:

  1. 初始化一个空栈(用于存储操作符)
  2. 左到右遍历中缀表达式的每个字符。
  3. 如果遇到操作数,直接输出
  4. 如果遇到操作符(如+、-、*、/),则将其与栈顶的操作符进行比较。如果当前操作符的优先级高于栈顶操作符,将当前操作符压入栈;否则,将栈顶操作符弹出并输出,直到遇到优先级低于或等于当前操作符的操作符或栈为空,然后将当前操作符压入栈。
  5. 如果遇到左括号(,将其压入栈。
  6. 如果遇到右括号(,则将栈顶的操作符弹出并输出,直到遇到左括号,然后将左括号弹出
  7. 遍历完中缀表达式后,将栈中剩余的操作符依次弹出并输出

最后实现的关键代码如下:

vector<string> Infix2Suffix(string str)
{vector<string> suffixExpression; //存储后缀表达式stack<char> operatorStack;for (int i = 0; i < str.length(); i++){string temp = "";switch (str[i]){//case '+':case '-':case '*':case '/':{if (operatorStack.empty() || operatorStack.top() == '('){operatorStack.push(str[i]);}else{//栈不为空且栈顶元素优先级大于这个位置的运算符,优先级大的先入栈while (!operatorStack.empty() && comparePriorities(operatorStack.top()) >= comparePriorities(str[i])){temp += operatorStack.top();suffixExpression.push_back(temp);operatorStack.pop();temp = "";}operatorStack.push(str[i]);}break;}case '(':{operatorStack.push(str[i]);break;}case ')':{while (operatorStack.top() != '('){temp += operatorStack.top();suffixExpression.push_back(temp);operatorStack.pop();temp = "";}operatorStack.pop();break;}//除去+-*、/和括号,剩下的就是数字default:{if ((str[i] >= '0' && str[i] <= '9')){temp += str[i];//若是连续数字,若不是则跳过而直接放入逆波兰表达式while (i + 1 < str.size() && str[i + 1] >= '0' && str[i + 1] <= '9' || str[i + 1] == '.'){temp += str[i + 1];++i;}suffixExpression.push_back(temp);}}}}while (!operatorStack.empty()){string temp = "";temp += operatorStack.top();suffixExpression.push_back(temp);operatorStack.pop();}return suffixExpression;
}

最后是double result(vector<string> suffixExpression),这个函数将通过上述方法得到的后缀表达式进行计算从而得到最后的结果,大致流程如下:

  1. 从左到右遍历后缀表达式的每个字符。
  2. 如果遇到操作数,将其压入栈中。
  3. 如果遇到操作符,从栈中弹出两个操作数,进行相应的运算,然后将结果压回栈中。
  4. 遍历完后缀表达式后,栈中剩余的元素即为计算结果

具体实现的关键代码如下:

double result(vector<string> suffixExpression)
{stack<double> suffixStack; //后缀表达式计算栈double num, op1, op2;      //定义数字、操作数1和2for (int i = 0; i < suffixExpression.size(); i++){string temp = suffixExpression[i];if (temp[0] >= '0' && temp[0] <= '9'){// c_str()函数是将string转化为标准char*,atof()是将字符串转化为浮点数num = atof(temp.c_str());suffixStack.push(num); //数字压入栈}else if (suffixExpression[i] == "+"){//因为是后缀表达式,所以先出栈的是操作数2,后出来的是操作数1op2 = suffixStack.top(); //取栈顶suffixStack.pop();       //栈顶出栈op1 = suffixStack.top();suffixStack.pop();suffixStack.push(op1 + op2);}else if (suffixExpression[i] == "-"){op2 = suffixStack.top();suffixStack.pop();op1 = suffixStack.top();suffixStack.pop();suffixStack.push(op1 - op2);}else if (suffixExpression[i] == "*"){op2 = suffixStack.top();suffixStack.pop();op1 = suffixStack.top();suffixStack.pop();suffixStack.push(op1 * op2);}else if (suffixExpression[i] == "/"){op2 = suffixStack.top();suffixStack.pop();op1 = suffixStack.top();suffixStack.pop();suffixStack.push(op1 / op2);}}return suffixStack.top();
}

最后通过double solve(string str)函数来将上述的操作进行一个集成,提高整体代码的封装性。

double solve(string str)
{str = format(str);vector<string> suffixExpression = Infix2Suffix(str);double k = result(suffixExpression);return k;
}

🔑 function.h

这个模块主要是将一些常用的科学计算函数进行封装,从而提高代码的简洁程度以及减少不同模块代码之间的耦合性。 

以其中一个函数为例来展示关键代码:

double getSin( double number )
{double answer = 0;// 计算弧度double radian = ( number * 3.1415926 ) / 180.0;answer = sin( radian );return answer;
}

✅ 关键功能展示

👓 四则运算基础功能实现

👓 科学运算功能展现

👓 复合运算功能展现

性能改进与优

🏳‍🌈 时间显示功能

为了能够更好的提高软件的界面美观程度,从实用性的角度出发,我考虑在程序的右下角添加了一个时间显示的框框,在一定程度上能够是程序变得更加具有完备性

以下关键代码用以获取实时时间来展示:

MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);flag     = EMPTY;showtime = new QLabel(this);//更新时间,在statusbar右边显示,1000ms刷新一次QTimer *timer = new QTimer(this);connect(timer, &QTimer::timeout, this, &MainWindow::timerUpdate);timer->start(1000);ui->statusbar->addPermanentWidget(showtime);dealMenuBar();
}void MainWindow::timerUpdate()
{QDateTime time = QDateTime::currentDateTime();showtime->setText(time.toString("yyyy-MM-dd hh:mm:ss dddd"));
}

实现效果如下:

🏳‍🌈 历史记录及清空操作

在一个功能较为完善的计算器中,需要有历史记录功能和清空操作,通过以下关键代码来实现这些功能:

//记录历史信息
ui->historyDisplay->append(ui->textDisplay->text());
ui->statusbar->showMessage("记得Clear再进行下一次运算哦~", 3000);void MainWindow::dealMenuBar()
{//连接按键退出槽connect(ui->actionExit, &QAction::triggered, this, &MainWindow::close);//连接历史记录清空槽connect(ui->actionClearHistory, &QAction::triggered, this,&MainWindow::clearHistory);//连接对话框打开槽connect(ui->actionAbout, &QAction::triggered, &myDialog, &QDialog::open);
}

具体的实现可参考上述关键内容展示中的GIF。

🏳‍🌈 帮助文档以及ui界面美化

在实现了基础的功能的基础上,作者希望能够通过进一步美好用户的操作界面已经相关的帮助操作来带给用户更加优越的使用体验,在这种想法的基础上,作者增加了帮助文档这一操作选项以及对原有的ui界面进行进一步的美化。

设计帮助文档
通过设置相关参数等操作来进一步美化界面

💊 单元测试

上述的内容中,本文已经提到了通过构建CalTest类和CalObj类来实例化计算器对象,从而能够正确调用QT自带的QTest库中的测试相关函数,作者共设置了11个测试样例,涵盖了所有可能发生的情况和操作,具体的结果展示如下:

可以看到,通过了所有的测试,证明该程序在功能上具有一定的科学性和完备性,能够满足正常的使用需求。

🚩 异常处理措施与展示

对于用户在实际应用的过程中出现的相关非法操作,程序会通过相关提示来告知用户进行清空或者其他操作来重新进行运算:

出现左右括号不匹配的情况:

出现错误的使用相关运算符的情况:

出现÷0的情况,并不会直接报错,而是通过输出inf来提示用户:

出现次方或幂方的运算无底数的情况:

出现不完整的算式:

还有其他的异常情况,本文列举了上述几种典型的情况来作为说明,同时,也还有作者所遗漏的一些情况需要改进,将在未来的后续版本中进行进一步的更新。


🎵 心得体会

        在个人成长方面:这次的编程任务对我来说是一次非凡的体验。从最开的选择语言,对qt相关语言的学习,到后面自己慢慢去配置环境,去查阅相关的资料,看别人的博客和代码,学习优秀的经验,接着是对项目的不断打磨和优化,出现了bug的时候需要针对不同的问题一步步的去解决相关的问题,到最后的单元测试和性能测试,让我对于一个软件整体的生命流程有了进一步的认识,知道了一款好的产品是应该不断出来的,不断的测试,在测试中去优化相关的功能和用户体验;最后是对于程序的打包,通过windeployqt.exe,一款Qt自带的工具,用于创建应用程序发布包,将某程序依赖的库、资源拷贝到其所在目录,防止程序在其他电脑上运行报找不到库的错误,在这个过程中我对于一个项目的发布又有了进一步的体验和收获。

        在技术方面:通过使用Qt编程实现了一个科学计算器,在这个过程中,我学到了很多关于GUI设计和事件处理的知识,同时也深刻理解了面向对象编程的思想。例如:我学会了如何使用Qt Designer来设计GUI界面。通过拖拽和调整控件的位置和大小,我可以轻松地创建出符合需求的界面。同时,我也学会了如何使用信号和槽机制来实现各个控件之间的交互,这大大提高了我的编程效率。同时,在实现科学计算器的过程中,我也深刻理解了面向对象编程的思想。我将整个计算器看作是一个对象,将各种功能封装成不同的方法,然后通过调用这些方法来实现计算器的完整功能。这种思想不仅可以应用到科学计算器的开发中,也可以应用到其他程序的开发中。

        总而已知,实践出真知,通过这次的实践任务,让我完整的体验了一次项目的开发流程,这也符合软件工程这门课的目标所在,希望在接下来的课程中能够不断提高自己的能力,开发出更好的项目产品。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/94016.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

最短路径专题2 最短距离-多终点(堆优化版)

题目&#xff1a;样例&#xff1a; 输入 6 6 0 0 1 2 0 2 5 0 3 1 2 3 2 1 2 1 4 5 1 输出 0 2 3 1 -1 -1 思路&#xff1a; 根据题意&#xff0c;数据范围也小&#xff0c;也可以用朴素版的Dijsktra来做&#xff0c;朴素版的Dijsktra我做过了一遍了&#xff0c;可以看以一下我…

MySQL - mysql服务基本操作以及基本SQL语句与函数

文章目录 操作mysql客户端与 mysql 服务之间的小九九了解 mysql 基本 SQL 语句语法书写规范SQL分类DDL库表查增 mysql数据类型数值类型字符类型日期类型 示例修改&#xff08;表操作&#xff09; DML添加数据删除数据修改数据 DQL查询多个字段条件查询聚合函数分组查询排序查询…

【数据科学】Scikit-learn[Scikit-learn、加载数据、训练集与测试集数据、创建模型、模型拟合、拟合数据与模型、评估模型性能、模型调整]

这里写目录标题 一、Scikit-learn二、加载数据三、训练集与测试集数据四、创建模型4.1 有监督学习评估器4.1.1 线性回归4.1.2 支持向量机(SVM)4.1.3 朴素贝叶斯4.1.4 KNN 4.2 无监督学习评估器4.2.1 主成分分析(PCA)4.2.2 K Means 五、模型拟合5.1 有监督学习5.2 无监督学习 六…

React18入门(第一篇)——JSX、TSX语法详解

文章目录 一、JSX 语法简介二、和 HTML 标签的几点不同三、JSX 属性四、JSX 事件4.1 简单点击事件4.2 类型限制4.3 带参数&#xff0c;箭头函数 五、插入 JS 变量六、JSX 中使用条件判断七、循环 一、JSX 语法简介 JSX - 是 JS 的扩展&#xff0c;写在 JS 代码里面&#xff0c…

STM32 DMA从存储器发送数据到串口

1.任务描述 &#xff08;1&#xff09;ds18b20测量环境温度存储到存储器&#xff08;数组&#xff09;中。 &#xff08;2&#xff09;开启DMA将数组中的内容&#xff0c;通过DMA发送到串口 存在问题&#xff0c;ds18b20读到的数据是正常的&#xff0c;但是串口只是发送其低…

WSL安装异常:WslRegisterDistribution failed with error: 0xc03a001a

简介&#xff1a;如果文件夹右上角是否都有两个相对的蓝色箭头&#xff0c;在进行安装wsl时&#xff0c;设置就会抛出 Installing WslRegisterDistribution failed with error: 0xc03a001a的异常 历史攻略&#xff1a; 卸载WSL WSL&#xff1a;运行Linux文件 WSL&#xff1…

Java下正面解除警告Unchecked cast: ‘java.lang.Object‘ to ‘java.util.ArrayList‘

就是我在反序列化时&#xff0c;遇到这样一个警告&#xff1a; Unchecked cast: java.lang.Object to java.util.ArrayList<com.work1.Student>然后我去网上查&#xff0c;有些人说用SuppressWarnings(“unchecked”)去忽略警告&#xff0c;但是我觉得作为一名合格的程序…

postgresql-自增字段

postgresql-自增字段 标识列IdentitySerial类型Sequence序列 标识列Identity -- 测试表 create table t_user( -- 标识列自增字段user_id integer generated always as identity primary key,user_name varchar(50) not null unique );-- 自动生成序列 CREATE SEQUENCE public…

【重拾C语言】三、分支程序设计(双分支和单分支程序设计、逻辑判断、多分支程序设计、枚举类型表示;典型例题:判断闰年和求一元二次方程根)

目录 前言 三、分支程序设计 3.1 判断成绩是否及格——双分支程序设计 3.2 成绩加上获奖信息—单分支程序设计 3.3 逻辑判断——布尔类型 3.4 获奖分等级——多分支程序设计 3.5 表示汽车种类——枚举类型 3.6 例题 3.6.1 例题——判断某个年份是否闰年 3.6.2 例题—…

MySQL 性能优化

MySQL 性能优化 数据库命名规范 所有数据库对象名称必须使用小写字母并用下划线分割所有数据库对象名称禁止使用 MySQL 保留关键字&#xff08;如果表名中包含关键字查询时&#xff0c;需要将其用单引号括起来&#xff09;数据库对象的命名要能做到见名识意&#xff0c;并且最…

【记录】IDA|IDA怎么查看当前二进制文件自动分析出来的内存分布情况(内存范围和读写性)

IDA版本&#xff1a;7.6 背景&#xff1a;我之前一直是直接看Text View里面的地址的首尾地址来判断内存分布情况的&#xff0c;似乎是有点不准确&#xff0c;然后才想到IDA肯定自带查看内存分布情况的功能&#xff0c;而且很简单。 可以通过View-Toolbars-Segments&#xff0c…

同学苹果ios的ipa文件应用企业代签选择签名商看看这篇文章你再去吧

同学我们要知道随着互联网的发展&#xff0c;苹果应用市场的火爆&#xff0c;越来越多的开发者加入到苹果应用开发行业中来。同时&#xff0c;苹果应用市场上的应用也在不断增多&#xff0c;用户数量也在不断增加&#xff0c;苹果应用代签是指通过第三方公司为开发者的应用进行…

计算机视觉——飞桨深度学习实战-起始篇

后面我会直接跳到实战项目&#xff0c;将计算机视觉的主要任务和目标都实现一遍&#xff0c;但是需要大家下去自己多理解和学习一下。例如&#xff0c;什么是深度学习&#xff0c;什么是计算机视觉&#xff0c;什么是自然语言处理&#xff0c;计算机视觉的主要任务有哪些&#…

14.(开发工具篇github)如何在Github配置ssh key

第一步&#xff1a;检查本地主机是否已经存在ssh key 上图表示已存在。跳第三步 第二步&#xff1a;生成ssh key ssh-keygen -t rsa -C "xxxxxx.com"第三步&#xff1a;获取ssh key公钥内容&#xff08;id_rsa.pub&#xff09; cat id_rsa.pub第四步&#xff1a;G…

CompletableFuture 异步编排

目录 CompletableFuture 的详解代码测试配置类的引入Demo1Demo2CompletableFuture的async后缀函数与不带async的函数的区别ThreadPoolTaskExecutor 和 ThreadPoolExecutor 的区别Spring 线程池的使用业务使用多线程的原因场景一:场景二:FutureTask介绍线程池为什么要使用阻塞队…

数据结构 1.1 初学数据结构

数据结构的基本概念 数据结构在学什么&#xff1f; 如何用程序代码把现实世界的问题信息化 如何用计算机高效处理信息从而创造价值 数据&#xff1a; 数据元素、数据项&#xff1a; 数据元素——描述一个个体 数据对象——数据元素之间具有同样的性质 同一个数据对象里的数…

Springboot+Vue+Mysql实现模拟汽车保养系统(附源码)

前言 本项目基于springbootvue搭建的汽车保养的系统&#xff0c;页面较为粗糙&#xff0c;前端好的小伙伴可自行优化。 项目环境 -环境框架后端JDK1.8SpringBootmybatisPlus前端NodeJS16.0Vue2.0ElementPlus数据库MySQL8.0- 数据库设计 数据表备注banner轮播图表car用户汽…

linux命令行配置音频设备

linux命令行配置音频设备 TLTR在linux命令行播放音乐cmus需要开始声音条件功能才能调节播放的音量&#xff0c;看这个链接&#xff0c;继续折腾&#xff0c;have fun! TLTR 以archLinux为例&#xff0c;把下面软件都装一遍。 sudo pacman -S alsa-utils sudo pacman -S alsa-…

算法-数学-斜率-直线上最多的点数

算法-数学-斜率-直线上最多的点数 1 题目概述 1.1 题目出处 https://leetcode.cn/problems/max-points-on-a-line/ 1.2 题目描述 给你一个数组 points &#xff0c;其中 points[i] [xi, yi] 表示 X-Y 平面上的一个点。求最多有多少个点在同一条直线上。 2 暴力搜索斜率…

十二、【漏洞复现】Rails任意文件读取(CVE-2019-5418)

十二、【漏洞复现】Rails任意文件读取(CVE-2019-5418&#xff09; 12.1、漏洞原理 Ruby on Rails是一个使用 Ruby 语言写的开源 Web 应用框架&#xff0c;它是严格按照 MVC 结构开发的。它努力使自身保持简单&#xff0c;来使实际的应用开发时的代码更少&#xff0c;使用最少…