c++ 实现计算器(数据结构 您所在的位置:网站首页 阶乘和计算器 c++ 实现计算器(数据结构

c++ 实现计算器(数据结构

2023-06-11 06:50| 来源: 网络整理| 查看: 265

数据结构课程设计——计算器

计算器 的图像结果

目录(可跳转)

文章目录 1 项目介绍1.1 背景分析1.2 功能分析 2 类结构设计3 功能实现3.1 输入功能3.2 合法性检测3.3 中缀转后缀+计算 4 亮点和小结4.1 代码亮点简述4.2 项目小结/心得

1 项目介绍 1.1 背景分析

  计算器,广泛应用于商业交易中,是必备的办公用品之一,也被称作“第一代电子计算机”。与计算机相同,它的内部也由逻辑电路组成,其功能实现也离不开对各条语句的分析,从而解析复杂的表达式,按照计算规则得到正确的结果。

  我们平时最常接触的计算表达式就是“中缀表达式”,然而机器要解析中缀表达式并不容易,尤其是在有括号和各种不同优先级的运算符同时存在的时候,通过对中缀表达式进行转换,使其变成”后缀表达式“,则可以用栈来实现计算的过程。

​  本项目就是一个用栈来实现表达式计算的简易计算器。

1.2 功能分析

基础功能

支持的运算有+,-,*,/,%,^ 支持括号运算 支持单目运算符+和-的运算

项目附加功能(测试)

判断计算式的括号是否匹配 检测是否有非法字符出现 自动删除表达式中的空格 2 类结构设计

  本项目拟用栈来实现计算过程,由于没有限制表达式的长度大小,故采用”链式栈“的储存方式,其中,”链式栈类(LinkedStack.h和LinkedStack.cpp)“均直接引用项目”勇闯迷宫“里的类的声明与定义。   项目除了”链式栈类“之外,还有”计算器类(即Calculator)“,用来实现对计算器各种功能的封装,提高可复用性和可读性。

在这里插入图片描述

  

Calculator类的声明

class Calculator { private: LinkedStack s_input_; //从键盘读入的,同时含有操作数和操作符的栈 LinkedStack s_char_; //处理过后,只含有操作符的栈 LinkedStack s_double_; //处理过后,只含有操作数的栈 public: Calculator(); ~Calculator(); double getRes(); //中缀转后缀并返回答案 bool Input(); //从键盘读入一个中缀表达式,存入栈中 bool isLegal(); //判断中缀表达式是否合法的函数 bool Calculate(char); //计算后缀表达式结果 int charToDouble(char); //字符转整型函数,调用后接下来出现的数字都参与转换 int getIsp(char); //返回一个操作符的栈内优先级,返回-1表示是非法字符 int getIcp(char); //返回一个操作符的栈外 优先级,返回-1表示是非法字符 };

  

3 功能实现 3.1 输入功能

  

1.运行截图 解释:输入一个以’='结尾的算式,则输入成功

 

在这里插入图片描述 解释:程序不认为回车所控制的”转行“为输入终止条件,程序仅认=符号为输入完毕标志

  在这里插入图片描述 解释:将表达式全部分行写也可以正常读入,只要以=结尾

    2.代码展示(ps:略去了部分声明和系统提示)

bool Calculator::Input() { cout s_input_.Pop(cur_char); //从输入字符串当中pop一个字符 if (cur_char >= '0' && cur_char temp.Push(cur_char); //是合法字符,进入辅助栈 } else if (cur_char == ')') { temp.Push(cur_char); //是合法字符,进入辅助栈 parentheses.Push(')'); //由于是逆序,所以是'('栈,')'出栈 } else if (cur_char == '(') { temp.Push(cur_char); //是合法字符,进入辅助栈 parentheses.Pop(cur_char); //与')'匹配,出栈 } else if (cur_char == '=') { //不用管,删除即可 continue; } else if (cur_char == ' ') { continue; //空格不算非法字符,删除即可 } else { //如果都不是,说明为非法字符,输出提示 cerr cerr (.icp符号进栈30,2,105#, +, *, ( , -\95\数字进栈30,2,105,95#, +, *, ( , -\)) .icp < -.icp出栈,运算30,2,10#, +, *, (105-95=10) .icp = (.icp出栈30,2,10#, +, *-- .icp < *.icp出栈,运算30,20#, +2*10=20- .icp < +.icp出栈,运算50#30+20=50-.icp > #.isp符号进栈50#, -\560\数字进栈50,560#, -\// .icp > #.icp符号进栈50,560#, - , /\56\数字进栈50,560,50#, -, /\## .icp < /.icp出栈,运算50,10#, -560/56=10# .icp < -.icp出栈,运算40#, -50-10=40#.icp = #.isp循环终止40\\

运行截图

 

2.代码展示(ps:略去了部分声明和系统提示)

getRes()函数

double Calculator::getRes() { while (1) { if (cur_char >= '0' && cur_char s_char_.getTop(top_char); if (getIsp(top_char) s_char_.Pop(op); //从字符栈之中退出,进行运算 Calculate(op); Pop_flag = false; } else { //优先级一样 Pop_flag = true; s_char_.Pop(op); if (op == '(') { s_input_.Pop(cur_char); //继续处理下一个字符 } if (op == '#') { break; //全部计算完毕,退出循环 } } } } s_double_.Pop(res); return res; //返回最终答案 }

Calculate()函数

bool Calculator::Calculate(char op) { //计算后缀表达式结果 double left, right; //左右两个操作数 double res = 0; //和计算后结果 s_double_.Pop(right); //右操作数的出栈 s_double_.Pop(left); //左操作数的出栈 switch (op) { case'+': res = left + right; break; case'-': res = left - right; break; case'*': res = left * right; break; case'/': res = left / right; break; case'%': res = fmod(left, right); break; case'^': res = pow(left, right); break; } s_double_.Push(res); return true; } 4 亮点和小结 4.1 代码亮点简述

​ 以下简述一下本项目的一些亮点(仅代表个人观点):

计算器实现了单目运算符 ‘+’ 和 ’ - ’

合法性检测考虑了”非法字符“,”括号不匹配“,”输入算式中含有括号“三种情况,并对应有提示信息

项目文档中附加有 ”流程图“ 和 ”工作栈示意图“ ,使读者易懂,思路清晰

项目的代码风格良好,变量和函数命名统一,有规整的注释帮助理解代码

 

4.2 项目小结/心得

  本项目已经不是第一次使用 ”链式栈“ (勇闯迷宫也用了链式栈),因此在类结构方面没有遇到什么难题,也比较熟练。

  项目在对计算的整个思维结构要求较高,我认为难点有三:

  一是各个函数之间的调用关系,由于函数要求简洁性和专一性,因此其体量较小,因此在”大函数分解为小函数“的过程中会遇到些难题;

  二是单目运算符的处理,本项目采用将+A看作是0+A的运算,将-A看作是0-A的运算,避免了对特殊情况的考虑,极大地减少函数分支数,提高效率,识别单目运算符则采用”判断操作数与操作符数目“的方法进行,一旦操作符个数大于操作数个数,则说明碰到了单目运算符,对其做出特殊标记;

  三是”中缀转后缀“的分支处理,由于我们日常生活中采用的运算方式是中缀表达式,所以对于后缀的形式表现得极其生疏,不论是转化过程还是计算后缀表达式都是一个非常”别扭“的事情,但是随着项目的结束,对于后缀表达式的熟练度已经远超过刚开始做项目的时候,这也算是一个大收获。

  计算器是一个非常具有实际意义的项目,一方面,这是我们生活中经常接触到的东西,另一方面,它的功能的综合性就要求我们在写代码的时候既要有全局观,又要考虑很多细节,因此十分锻炼代码能力,同时,经过这次项目,再一次巩固了栈的知识,加深了对栈的特点的认识。

  二是单目运算符的处理,本项目采用将+A看作是0+A的运算,将-A看作是0-A的运算,避免了对特殊情况的考虑,极大地减少函数分支数,提高效率,识别单目运算符则采用”判断操作数与操作符数目“的方法进行,一旦操作符个数大于操作数个数,则说明碰到了单目运算符,对其做出特殊标记;

  三是”中缀转后缀“的分支处理,由于我们日常生活中采用的运算方式是中缀表达式,所以对于后缀的形式表现得极其生疏,不论是转化过程还是计算后缀表达式都是一个非常”别扭“的事情,但是随着项目的结束,对于后缀表达式的熟练度已经远超过刚开始做项目的时候,这也算是一个大收获。     计算器是一个非常具有实际意义的项目,一方面,这是我们生活中经常接触到的东西,另一方面,它的功能的综合性就要求我们在写代码的时候既要有全局观,又要考虑很多细节,因此十分锻炼代码能力,同时,经过这次项目,再一次巩固了栈的知识,加深了对栈的特点的认识。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有