目录

一.问题分析

二.算法思想

1.关于分词器

2.关于逆波兰式分析器:

三.实现代码

1.头文件  &  类视图

2.预处理部分

3.逆波兰分析过程

4.计算,输出部分

四.总结


一.问题分析

将用中缀式表示的算术表达式转换为用逆波兰式(后缀式)表示的算术表达式,并计算用逆波兰式来表示的算术表达式的值.

如输入如下:     21+(( 42-2) *15+6  ) -18#

输出为:

原来表达式:      21+ (( 42-2 ) *15+6 ) - 18#

后缀表达式:      21&42&2&-15&*6&++18&-

计算结果:  609

二.算法思想

1.关于分词器

将输入串的所有非空白元素添加到一个vector <string> 数组里

在输入串(string)里设置两个(广义上的)指针,一前一后指向非空白元素.遇到空白字符自动向后跳

前   \       后

数字

字符

数字

temp+=input[cur]

temp+= input[cur]

token.push_back(temp)

temp.clear()

字符

temp+=input[cur]

token.push_back(temp)

temp.clear()

比如(5+…)

假设++,--操作是不存在的

Ep:碰到(3+5)*3

temp+=input[cur]

token.push_back(curString)

cur.clear()

2.关于逆波兰式分析器:

或阅读该文:逆波兰分析

三.实现代码

1.头文件  &  类视图

using namespace std;
#include<iostream>
#include<string>
#include<stack>
#include<vector>
#include<algorithm>/*
逆波兰式不需要括号,当从中缀式转换为RPN时,遇到 ( 括号要暂存之后的运算符;遇到 ) 括号要将内部的运算符(在TempStack)挨个转移到finStack逆波兰计算的是表达式,可以写一个简化的分词器函数(只分析数字,运算符,空格),
将各个长度不一(如"55"长度为2,"+"长度为1)的元素分别进入一个  vector<string> token;
这个函数无需分析词性.分析的工作在核心函数Analysis()中对token[i][0]判别完成,
Analysis()同时还要调用isSym()来进行判别工作tempStack  总是将优先级高的运算符放在栈顶,压住优先级低的运算符.当转移到finStack时,优先级高的先转移
*/class RPN
{
public:RPN(string);~RPN();bool isLegal();                   //检测输入串是否含有非法字符(不是运算符||操作数的字符)bool isNum(char);             //经过分词程序后形成的vector<string> token. input:token[i][0] 每个元素的第一个字符void divideElement();           //分词器int Prio(char);                    //判断运算符的优先级,数字越小,优先级越弱void analysis();                  //分析程序,将token->逆波兰式void Print();                     //打印整个逆波兰式void Computing();             //根据逆波兰式计算解float getRes();                  //返回最终值
private:stack<string>tempExp;     //临时栈,记录临时存放的运算符stack<string>finExp;      //最终栈,操作数依序放入即可vector<string>Result;  //记录fin栈的结果vector<string>token;   //接收分词后的字符串string input;                    //接收输入的字符串float res;                        //接收计算逆波兰式的最终结果};

2.预处理部分

包括判断是否数字,输入是否合法,将输入串分词为独立的元素,存入token 数组

/*isLegal():     检测是否存在非法字符,即除了数字和运算符之外的其他所有字符input:         类数据成员 vector<string> Inputout:                存在非法字符,报错.  (调用isLegal的函数会据此决定是否返回)
*/
bool RPN::isLegal()
{bool flag = true;for (size_t i = 0; i < input.size(); i++)      //遍历输入串的每一个char{if ((input[i] >= '0') && (input[i] <= '9'))             //判断数字{continue;}else                                                           //判断运算符{if (input[i] != '+' || input[i] != '-' || input[i] != '*' || input[i] != '/' ||input[i] != '(' || input[i] != ')' || input[i] != '#' || input[i] != ' '){flag = false;}}}return flag;;
}/*isSym():     经过isLegal() 处理后,只剩下数字,运算符,空格return true:    是运算符return false:   是数字
*/
bool RPN::isNum(char ch)
{if (ch>='0' &&ch<='9'){return true;}else{return false;}
}/*divideElement(): 将输入串分为一个个不含空格的元素,存储在一个vector<string>里input:                   成员变量:input output:                  成员变量:  vector<string> token*/
void RPN::divideElement()
{size_t next = 1;      //一开始,指向input[1]string temp;            //临时存放字符for (size_t cur = 0; cur < input.size(); cur++,next++)      //cur从[0]->[n-2]{if (next == input.size() - 1) { next--; }//当next到达最后一个,cur到达倒数第二个时.让next回退 1,可以有效阻止range溢出if (input[cur] == ' ' || input[cur] == '\t')    //跳过空格{cur++;next = cur + 1;        //next总指向cur下一个}if (input[next] == ' '|| input[next] == ' \t'){next++;}if (isNum(input[cur]))//第一个是数字{if (isNum(input[next]))  //第二个是数字{temp += input[cur];         //将前一位添加到tempif (cur == input.size() - 1) //当到达末尾时{token.push_back(temp);temp.clear();}}else                              //第二个是字符{temp += input[cur];              //前一个进入temptoken.push_back(temp);       //token添加这个temptemp.clear();                            //清空temp,待留下次使用}}else{temp += input[cur];token.push_back(temp);temp.clear();}}
}

3.逆波兰分析过程

包括优先级判断函数,以及核心的逆波兰分析程序

/*Prio():    对每个字符进行判别input: token[i][0],即token每个元素的第一个字符return: 该字符的优先级
*/
int RPN::Prio(char ch)
{switch (ch){case '#':return 0;case'+':case'-':return 1;case'*':case'/':case'×':return 2;case'(':case')':return 3;default:break;}return 0;
}/*analysis():  程序的核心部分,进行逆波兰式的转换input():       token向量,temp栈,fin栈return:       finStack
*/
void RPN::analysis()
{tempExp.push("#");           //先将优先级最低的"#"放在栈底for (size_t i = 0; i < token.size(); i++)  //遍历token{if (isNum(token[i][0]))               //要么是数字{finExp.push(token[i]);}else                                     //要么是运算符{if (token[i] == "(")                   //若x是'(',则直接压入temp{tempExp.push(token[i]);}else if (token[i] == ")")       //若x是')',则将距离栈s1栈顶的最近的'('之间的运算符,逐个出栈,依次压入栈fin,{while (tempExp.top() != "("){finExp.push(tempExp.top());tempExp.pop();}tempExp.pop();             //此时抛弃 "("}else                                   //此时不是( 与 ){if (tempExp.top()=="(")         //此时temp栈顶是(,直接将扫描字符压入temp,因为()内是优先级最高的{tempExp.push(token[i]);}else                                        //栈顶元素不为'('{if (Prio(token[i][0])>Prio(tempExp.top()[0]))          //如果 token[i]的优先级 > temp栈顶的优先级,直接压入{tempExp.push(token[i]);}else{while (Prio(token[i][0]) <= Prio(tempExp.top()[0]) && tempExp.top()!="(")    //否则,不断地将temp的栈顶转移到fin(不要括号),直到(  temp的栈顶 的优先级 小于 token[i] 的优先级    或     栈顶为"("{finExp.push(tempExp.top());tempExp.pop();}tempExp.push(token[i]);}}}}}//检查tempExp是否为空,否则依次转移到finExpif (!tempExp.empty()){while (tempExp.top()!="#"){finExp.push(tempExp.top());tempExp.pop();}}
}

4.计算,输出部分

包括将栈中的元素取出到一个vector,逆波兰式计算值

/*Print:     打印结果input:  finExp栈return:  一串cout结果
*/
void RPN::Print()
{//倒腾数据结构while (!finExp.empty()){Result.push_back(finExp.top());finExp.pop();}//想要逆转过来reverse(Result.begin(), Result.end());//逆序打印for (size_t i = 0; i <Result.size(); i++){cout << Result[i] << "   ";}cout << endl;
}void RPN::Computing()
{while (Result.size()!=1)              //这样写比较耗费性能,容器物理上的增删需要花费额外的时间与内存.但是使用指针式的写法需要更多时间与精力,遂作罢{for (size_t i = 0; i < Result.size(); i++){if (!isNum(Result[i][0]))                         //需要找到第一个运算符{float temp=0.0;switch (Result[i][0]){case '+':temp = stof(Result[i - 2]) + stof(Result[i - 1]);//新值放在原运算符的位置上break;case '-':temp = stof(Result[i - 2]) - stof(Result[i - 1]);break;case '/':temp = stof(Result[i - 2]) / stof(Result[i - 1]);break;case'*':case'×':temp = stof(Result[i - 2]) * stof(Result[i - 1]);break;default:break;}Result[i] = to_string(temp);                                                 //新值放在原运算符的位置上Result.erase(Result.begin() + i - 2);                                       //删除前两个操作数Result.erase(Result.begin() + i - 2);i = 0;                                                                                 //每次执行完后重新从首元素开始}}}res=stof(Result[0]);
}float RPN::getRes()
{return this->res;
}

5.mian()部分

int main()
{string str="21+(( 42-2)*15+6   ) -18";cout << "表达式:  21+(( 42-2)*15+6    ) -18\n";RPN rpn(str);//输入串判别if (!rpn.isLegal()){cout << "含有非法字符,请重新输入\n";//此处可以有一个goto语句}//分词rpn.divideElement();//分析rpn.analysis();//打印结果rpn.Print();//计算值rpn.Computing();cout<<rpn.getRes();system("pause");return 0;
}

四.总结

其实最后的计算值部分应该用传址的方式写的,但那样需要更多的时间与精力,时间上不允许优化效率了.

编译原理 | 实验四 | 逆波兰式相关推荐

  1. 编译原理实验二-逆波兰式生成程序

    一.实验目的和要求: 1. 掌握语法分析的基本思想,并用高级语言编写逆波兰式生成程序 2. 要求利用逆波兰式生成算法编写程序,将从键盘上输入的算术表达式 (中缀表达式)转化成逆波兰式 二.实验平台: ...

  2. 编译原理逆波兰式实验java_逆波兰式算法的编译原理实验过程.doc

    逆波兰式算法的编译原理实验过程 实验目的 深入理解算符优先分析法 掌握FirstVt和LastVt集合的求法有算符优先关系表的求法 掌握利用算符优先分析法完成中缀表达式到逆波兰式的转化 实验内容及要求 ...

  3. 笔记-编译原理-实验四-语义分析与中间代码生成

    实验四. 语义分析及中间代码生成 设计思想 根据对属性文法及语义分析.中间代码生成的学习,可以将实验二.三的两种语法分析器进行一定的改造,以达到进行语法分析的同时进行语义分析并生成中间代码.根据PL0 ...

  4. 编译原理-实验四-LR(0)语法分析程序的设计

    一.实验目的 了解LR(0)语法分析算法的基本思想,掌握LR(0)语法分析程序的构造方法. 二.实验内容 根据LR(0)语法分析算法的基本思想,设计一个对给定文法进行LR(0)语法分析的程序,并用C. ...

  5. 编译原理 实验四 LR(0)分析法(LR0分析表的自动生成)

    写在前面 由于代码较长,csdn对文章总长度有字数限制,想只看完整代码的请移步另一篇博客. https://blog.csdn.net/qq_46640863/article/details/1257 ...

  6. 编译原理实验四:验证Yacc的使用

    所有实验的源代码:点此下载 实验目的: 熟悉语法分析器生成工具Yacc的使用,并学会在cygwin下使用bison工具编译Yacc文法说明文件.学习如何使用lex和yacc合作进行语法分析. 实验内容 ...

  7. 编译原理逆波兰式实验java_【实验三】—— 逆波兰式生成实验报告

    实验三 逆波兰式生成实验报告 一.实验名称:逆波兰式生成 二.仪器.设备:计算机 三.参考资料:<编译原理教程>习题解析与上机指导(西安电子科技大 胡元义等) 四.实验目的:将非后缀式用来 ...

  8. 逆波兰式 java_Java 实现《编译原理》中间代码生成 -逆波兰式生成与计算 - 程序解析...

    Java 实现<编译原理>中间代码生成 -逆波兰式生成与计算 - 程序解析 编译原理学习笔记 (一)逆波兰式是什么? 逆波兰式(Reverse Polish notation,RPN,或逆 ...

  9. 逆波兰式的产生及计算(C++/Java)---编译原理

    逆波兰式的产生及计算(C++版及Java版本)-编译原理,数据结构 在现实生活中大家对于计算使用都是中缀表达式(nifix expression),如但是在计算机中表达式常常是以后缀表达式(postf ...

最新文章

  1. 在CentOS上搭建PHP服务器环境
  2. audio标签控制音量_HTML5中audio与video标签的使用
  3. 汇编入门学习笔记 (十二)—— int指令、port
  4. 【翻译】.NET 5 Preview 1 发布
  5. Ecipse快捷键的使用
  6. Python序列基本操作(二)
  7. OpenCV自动内存管理
  8. C#调用非托管Dll
  9. 聊聊 print 的前世今生
  10. @WebListener 注解方式实现监听(eclipse和idea)
  11. magento 客户表相关_第9章 相关性分析
  12. 音乐网站Spotify将融资4亿美元,估值84亿美元
  13. [译] 移动应用设计新趋势
  14. PyHook3 的下载与安装
  15. 固件编辑器android,定制 Android 固件
  16. JAVA实现战舰游戏
  17. 视频转GIF小工具,原生javascript + gif.js + canvas,自定义制作表情包
  18. 在windows上编译apr库apr-util库
  19. Maven 中跳过单元测试方法
  20. js里双重否定的作用

热门文章

  1. jzoj. 3518. 【NOIP2013模拟11.6A组】进化序列(evolve)
  2. JZOJ 3518. 【NOIP2013模拟11.6A组】进化序列(evolve)
  3. 十五、Events类
  4. div点击穿透,CSS属性pointer-events :none;实现护眼模式, 夜间模式遮罩
  5. 新版Vue项目配置项目名称-publicPath-前端_v1.0.2
  6. Hadoop单节点设置
  7. 盘点2018年云计算热点:云原生、全栈云,云大脑,谁能独占鳌头?
  8. vscode配置prettier格式化工具
  9. c语言浮点变量是什么意思,C语言中说的浮点型是什么意思呢 C语言的浮点数是什么...
  10. 一元享移动怎么样_中国移动终于认怂?29元享100G流量还不限速,网友:后悔携号转网了...