逆波兰式

合集下载

c++逆波兰式计算

c++逆波兰式计算

c++逆波兰式计算C++逆波兰式计算是一种基于后缀表达式的计算方法。

逆波兰式也称为后缀表达式,其中操作符位于操作数之后。

下面我会从多个角度来解释逆波兰式计算。

1. 逆波兰式的转换:将中缀表达式转换为逆波兰式的过程称为逆波兰式的转换。

这个过程可以通过使用栈来实现。

具体步骤如下:从左到右扫描中缀表达式的每个元素。

如果遇到操作数,则直接输出到逆波兰式。

如果遇到操作符,则与栈顶操作符比较优先级。

如果栈顶操作符优先级高于当前操作符,则将栈顶操作符输出到逆波兰式,然后将当前操作符入栈;否则将当前操作符入栈。

如果遇到左括号,则将其入栈。

如果遇到右括号,则将栈顶操作符输出到逆波兰式,直到遇到左括号。

左括号出栈,但不输出到逆波兰式。

扫描结束后,将栈中剩余的操作符依次输出到逆波兰式。

2. 逆波兰式的计算:逆波兰式计算是通过对逆波兰式进行求值来得到结果的过程。

这个过程同样可以使用栈来实现。

具体步骤如下:从左到右扫描逆波兰式的每个元素。

如果遇到操作数,则入栈。

如果遇到操作符,则从栈中弹出两个操作数,进行相应的运算,并将结果入栈。

扫描结束后,栈中的唯一元素即为最终的结果。

3. C++实现逆波兰式计算:在C++中,可以使用栈来实现逆波兰式的计算。

具体步骤如下:定义一个栈来存储操作数。

从左到右扫描逆波兰式的每个元素。

如果遇到操作数,则将其转换为数字并入栈。

如果遇到操作符,则从栈中弹出两个操作数,进行相应的运算,并将结果入栈。

扫描结束后,栈中的唯一元素即为最终的结果。

总结:逆波兰式是一种基于后缀表达式的计算方法,可以通过转换中缀表达式得到。

逆波兰式计算可以使用栈来实现,通过扫描逆波兰式的每个元素,根据操作数和操作符进行相应的操作,最终得到计算结果。

在C++中,可以使用栈来实现逆波兰式的计算。

希望以上解释能够满足你的需求。

逆波兰表达式、波兰表达式【数据结构与算法】

逆波兰表达式、波兰表达式【数据结构与算法】

逆波兰表达式、波兰表达式【数据结构与算法】逆波兰表达式、波兰表达式【数据结构与算法】1.前缀表达式⼜称波兰式,前缀表达式的运算符位于操作数之前。

⽐如:- × + 3 4 5 62.中缀表达式就是常见的运算表达式,如(3+4)×5-63.后缀表达式⼜称逆波兰表达式,与前缀表达式相似,只是运算符位于操作数之后,⽐如:3 4 + 5 × 6 -⼈类最熟悉的⼀种表达式1+2,(1+2)3,3+42+4等都是中缀表⽰法。

对于⼈们来说,也是最直观的⼀种求值⽅式,先算括号⾥的,然后算乘除,最后算加减,但是,计算机处理中缀表达式却并不⽅便。

然后我们还需明确⼀些概念,下⾯通过我们最熟悉的中缀表达式画出⼀棵语法树来直观认识⼀下前后缀表达式的⽣成。

以A+B*(C-D)-E*F为例:中缀表达式得名于它是由相应的语法树的中序遍历的结果得到的。

上⾯的⼆叉树中序遍历的结果就是A+B*(C-D)-E*F。

前缀表达式是由相应的语法树的前序遍历的结果得到的。

上图的前缀表达式为- + A * B - C D * E F后缀表达式⼜叫做逆波兰式。

它是由相应的语法树的后序遍历的结果得到的。

上图的后缀表达式为:A B C D - * + E F * -下⾯我们关注两个点:1.如何根据⼀个逆波兰表达式求出运算结果?2.如果将⼀个中缀表达式转换成后缀表达式(逆波兰表达式)⼀.通过逆波兰表达式计算结果我们先看⼀个例⼦...后缀表达式3 4 + 5 × 6 -的计算1.从左⾄右扫描,将3和4压⼊堆栈;2.遇到+运算符,因此弹出4和3(4为栈顶元素,3为次顶元素,注意与前缀表达式做⽐较),计算出3+4的值,得7,再将7⼊栈;3.将5⼊栈;4.接下来是×运算符,因此弹出5和7,计算出7×5=35,将35⼊栈;5.将6⼊栈;6.最后是-运算符,计算出35-6的值,即29,由此得出最终结果。

从上⾯的过程我们如何编写代码实现呢?可以采⽤⼀个辅助的栈来实现计算,扫描表达式从左往右进⾏,如果扫描到数值,则压进辅助栈中,如果扫描到运算符,则从辅助栈中弹出两个数值参与运算,并将结果压进到栈中,当扫描表达式结束后,栈顶的数值就是表达式结果。

逆波兰表达式

逆波兰表达式

逆波兰表达式逆波兰表达式表达式⼀般由操作数(Operand)、运算符(Operator)组成,例如算术表达式中,通常把运算符放在两个操作数的中间,这称为中缀表达式(Infix Expression),如A+B。

波兰数学家Jan Lukasiewicz提出了另⼀种数学表⽰法,它有两种表⽰形式:把运算符写在操作数之前,称为波兰表达式(Polish Expression)或前缀表达式(Prefix Expression),如+AB;把运算符写在操作数之后,称为逆波兰表达式(Reverse Polish Expression)或后缀表达式(Suffix Expression),如AB+;其中,逆波兰表达式在编译技术中有着普遍的应⽤。

算法:⼀、将中缀表达式转换成后缀表达式算法:1、从左⾄右扫描⼀中缀表达式。

2、若读取的是操作数,则判断该操作数的类型,并将该操作数存⼊操作数堆栈3、若读取的是运算符(1) 该运算符为左括号"(",则直接存⼊运算符堆栈。

(2) 该运算符为右括号")",则输出运算符堆栈中的运算符到操作数堆栈,直到遇到左括号为⽌。

(3) 该运算符为⾮括号运算符:(a) 若运算符堆栈栈顶的运算符为括号,则直接存⼊运算符堆栈。

(b) 若⽐运算符堆栈栈顶的运算符优先级⾼或相等,则直接存⼊运算符堆栈。

(c) 若⽐运算符堆栈栈顶的运算符优先级低,则输出栈顶运算符到操作数堆栈,并将当前运算符压⼊运算符堆栈。

4、当表达式读取完成后运算符堆栈中尚有运算符时,则依序取出运算符到操作数堆栈,直到运算符堆栈为空。

⼆、逆波兰表达式求值算法:1、循环扫描语法单元的项⽬。

2、如果扫描的项⽬是操作数,则将其压⼊操作数堆栈,并扫描下⼀个项⽬。

3、如果扫描的项⽬是⼀个⼆元运算符,则对栈的顶上两个操作数执⾏该运算。

4、如果扫描的项⽬是⼀个⼀元运算符,则对栈的最顶上操作数执⾏该运算。

5、将运算结果重新压⼊堆栈。

中缀式到逆波兰式的转换及求值

中缀式到逆波兰式的转换及求值

号 等 ) 的 运 算 规 则 , 因 此 ,人 们 习 惯 用 中缀 式 对 于 运 算 的 实
现 ,特 别 是 计 算 机 或 计 算 器 的 实 现 来 说 ,实 际 上 是 比较 麻 烦 的。 同 样 ,在计 算 机 科 学 的 编译 领 域 中 。要 从 中缀式 直 接产 生 目标 代码 相 对 来 说 也 是 比较 麻 烦 的 。这 样 的 现 象 同样 体 现 在 一 些 财 务 或会 计 软 件 ( 工 资管 理 ) 中 。在 这 些 软 件 中 ,不 可避 如 免 地 都 要用 到公 式 的 定 义及 求 值 , 由于 财 务 、会 计计 算 中公 式 的复 杂 性 和 动 态 性 ,如 果 直 接用 中 缀 式 来 实 现 其 定 义 和 求 值 ,
表 1 中 缀式 、逆 波 兰 式对 照表
中缀式
a b + a b‘ + c
将 中缀 式 转换 成 逆 波 兰 式 并 进 行求 值 需 要 完 成 如 下 几 件工
作:
逆波兰式
a+ b a b* + c
( ) 对 中 缀 式 进 行 语 法 分 析 ( 的地 方 也 称 其 为 合 法 性 1 有
的运 算 的首 选 算法 。
逆 波 兰 式 不仅 能用 于 表 示 和 计 算 表 达式 .还 可 用 于 表 示 其
他 的语 法 结 构 ,此 时 的逆 波 兰 表 达 式 中 的运 算 符 不 再 仅仅 局 限
于算 术 运 算 符 、关 系运 算 符 和 逻辑 运算 符 ,而 且 ,每 个 运算 符
表 1是与 中缀 式对 应 的逆 波 兰 表示 。
另 外 ,逆 波 兰 式 的输 入 过 程 实 质 上 就 是 其 计 算 过 程 .因 此 ,可 以清 楚 地 看 到 整个 计 算 的 中间 结 果 。 它 的 这一 特 点 增 强 了交 互性 ,让 用 户 能够 在 运 算 的 过 程 中 方便 地发 现 和 改 正 运 算 错 误 ,并 且 还 能 很 好地 跟 踪 运 算 流 程 .动 态 地 自定义 运 算 过 程 中某 个 运 算 符 的 优 先 级 。在 1 7 9 2年 He l tP c ad公 司 发 现 we — akr t

逆波兰式

逆波兰式

淮阴工学院编译原理课程设计报告选题名称:逆波兰式的生成系(院):计算机工程学院专业:计算机科学与技术信息安全方向班级:信息1071姓名:王猛学号:1071303122 指导教师:高丽学年学期:2009 ~ 2010 学年第 2 学期2010年 6 月12 日设计任务书指导教师(签章):年月日摘要:编译原理是计算机科学与技术专业最重要的一门专业基础课程,内容庞大,涉及面广,知识点多。

由于该课程教、学难度都非常大,往往费了大量时间而达不到预期教学效果俗语说:学习的最好方法是实践。

本课程设计正是基于此,力求为学生提供一个理论联系实际的机会,通过布置一定难度的课题,要求学生独立完成。

我们这次课程设计的主要任务是编程实现对输入合法的中缀表达式进行词法分析、语法分析,构造相应的逆波兰式,计算后缀表达式的值输出结果。

逆波兰式也叫后缀表达式,即将运算符写在操作数之后。

通过实践,建立系统设计的整体思想,锻炼编写程序、调试程序的能力,学习文档编写规范,培养独立学习、吸取他人经验、探索前言知识的习惯,树立团队协作精神。

同时,课程设计可以充分弥补课堂教学及普通实验中知识深度与广度有限的缺陷,更好地帮助学生从全局角度把握课程体系。

关键字:逆波兰式;语法分析;中缀表达式目录1 课题综述 (1)1.1 课题来源 (1)1.2 课题意义 (1)1.3 预期目标 (1)1.4 面对的问题 (1)1.5 需解决的关键技术 (1)2 系统分析 (2)2.1 基础知识 (2)2.2 解决问题的基本思路 (3)2.3 总体方案 (3)3 系统设计 (4)3.1 算法实现 (4)3.2 流程图 (5)4 代码编写 (6)5 程序调试 (10)6 运行与测试 (10)总结 (11)致谢 (12)参考文献 (13)1 课题综述1.1 课题来源在通常的表达式中,二元运算符总是置于与之相关的两个运算对象之间,所以,这种表示法也称为中缀表示。

对中缀表达式的计值,并非按运算符出现的自然顺序来执行其中的各个运算,而是根据算符间的优先关系来确定运算的次序,此外,还应顾及括号规则。

逆波兰式转换规则的推导过程

逆波兰式转换规则的推导过程

逆波兰式转换规则的推导过程逆波兰表示法(Reverse Polish Notation,RPN)是一种数学表达式的表示方法,其特点是运算符放在操作数之后。

例如,中缀表达式 "2 + 3" 在逆波兰表示法中写作 "2 3 +"。

逆波兰表示法的推导过程如下:1. 定义操作数和运算符:首先,我们需要定义操作数和运算符。

操作数可以是任何数字,而运算符可以是加法、减法、乘法或除法。

2. 构建逆波兰表示法的规则:如果操作符是加法或减法,则将其放在两个操作数的后面。

例如,"2 + 3" 转换为 "2 3 +"。

如果操作符是乘法或除法,则将其放在两个操作数的中间。

例如,"2 3" 转换为 "2 3 "。

对于括号,我们可以将其视为一个操作数,并按照上述规则处理。

例如,"2 + (3 - 1)" 可以转换为 "2 3 1 - +"。

3. 处理优先级问题:在逆波兰表示法中,我们不需要括号来表示优先级。

这是因为通过将运算符放在操作数之后,我们可以自然地解决优先级问题。

例如,"2 3 + 4" 在逆波兰表示法中是 "2 3 4 +",其中乘法和加法的优先级是明确的。

4. 处理负数和正数:在逆波兰表示法中,我们可以将负数和正数视为特殊的操作数。

例如,"2 - (-3)" 可以转换为 "2 -3 -"。

5. 验证逆波兰表示法的正确性:为了验证逆波兰表示法的正确性,我们可以使用堆栈数据结构。

从左到右读取表达式,如果遇到操作数,则将其压入堆栈;如果遇到运算符,则从堆栈中弹出相应的操作数进行计算,并将结果压回堆栈。

重复这个过程直到表达式结束。

如果最终堆栈中只剩下一个元素,则表达式是有效的;否则,表达式无效。

三元式四元式逆波兰式

三元式四元式逆波兰式

三元式四元式逆波兰式
三元式(Three-address code)是一种中间代码表示形式,用于将源代码转换为机器代码或目标代码。

它由三个操作数组成,通常包括一个运算符和两个操作数。

例如:
T1 = a + b
这里的"="是赋值运算符,"+"是算术运算符,"a"和"b"是操作数,"T1"是结果。

四元式(Quadruple)是一种中间代码表示形式,与三元式相似,但包含了四个操作数。

四元式通常包括一个运算符和三个操作数。

例如:
T1 = a + b
对应的四元式可以表示为:
+, a, b, T1
这里的"+"是算术运算符,"a"和"b"是操作数,"T1"是结果。

逆波兰式(Reverse Polish Notation,RPN)是一种数学表达式的表示方法,其中运算符在操作数之后。

这种表示方法避免了使用括号来指定运算的顺序,而是通过操作符的位置来确定运算的顺序。

例如:
2 3 +
表示的是2加3,计算结果为5。

逆波兰式的优点是简化了计算机对表达式的解析和计算过程,常用于栈的实现。

希望以上解答对你有所帮助!如有更多问题,请随时提问。

1。

C语言之逆波兰表达式完整代码(附算法)

C语言之逆波兰表达式完整代码(附算法)

C语言课程设计之逆波兰表达式//逆波兰表达式(后缀表达式)reverse polish notation//程序实现的功能是将中缀表达式转变为后缀表达式,再求出其值//主要运用的知识点有:isdigit函数,pow函数,system("cls")函数,堆栈,格式的强制转换#include<stdio.h>#include<ctype.h>#include<stdlib.h>#include<math.h>void shift( char notation[]); //中缀表达式转换为后缀表达式的转换函数float calculate(float a[][2],int k); //计算后缀表达式int judge(char notation[]); //判断输入的中缀表达式是否符合要求int grade(char a); //返回运算符的等级void display(float a[][2],int k); //在屏幕上显示后缀表达式//主函数void main(){char notation [100];char choice;do{printf("请输入正确的中缀表达式:\n");printf("例如:2*3+4/3-(2+1)\n");scanf("%s",&notation);if(judge(notation)){shift(notation);}elseprintf("你的表达式有错误,请仔细检查!\n");fflush(stdin);printf("\n你是否需要继续计算(是输入Y/y,否输入其他任意键)\n");scanf("%c",&choice);getchar();system("cls");}while(choice=='Y'||choice=='y');printf("\n程序结束,谢谢使用!\n");}//判定函数int judge(char notation[]){int i,m,num=1,p1=0,p2=0;for(i=0;notation[i]!='\0';i++) //排除表达式外的字符{if(notation[i]!='('&&notation[i]!=')'&&notation[i]!='+'&&notation[i]!='-'&&notation[i]!='*'&&notation[i]!='/'&&!isdigit(notation[i])&&notation[i]!='.') {num=0;return num;}}if(notation[0]=='*'||notation[0]=='/'||notation[0]==')'||notation[0]=='.') //排除第一个字符为*,/,),.{num=0;return num;}for(i=0;notation[i]!='\0';i++) //排除'+','-','*','/','.'之间的连续出现以及'+','-','*','/','.'后面直接加')'{if(notation[i]!='('&&notation[i]!=')'&&!isdigit(notation[i])){if(notation[i+1]!='('&&!isdigit(notation[i+1])){num=0;return num;}}if(notation[i]=='('&&(notation[i+1]==')'||notation[i+1]=='.'||notation[i+1]=='*'||notation[i+ 1]=='/')){ //排除'('和')','.','*','/'一起连用num=0;return num;}if(notation[i]==')'&&(notation[i+1]=='('||notation[i+1]=='.'))//排除')'和'(','.'一起连用{num=0;return num;}}for(i=0;notation[i]!='\0';i++) //小数位不得超过4位{if(notation[i]=='.'&&notation[i+1]!='\0'&&notation[i+2]!='\0'&&notation[i+3]!='\0'&&notation[i+4]!='\0'&&notation[i+5]!='\0'){if(isdigit(notation[i+1])&&isdigit(notation[i+2])&&isdigit(notation[i+3])&&isdigit(notation[i+ 4])&&isdigit(notation[i+5])){num=0;return num;}}}for(i=0;notation[i]!='\0';i++) //排除一个小数中有两个小数点的情况{if(notation[i]=='.'){i++;while(isdigit(notation[i])){i++;}if(notation[i]=='.'){num=0;return 0;}}}for(i=0;notation[i]!='\0';i++) //排除')'后面不可以直接跟数字以及'('前面不可以加数字{if(notation[i]==')'&&isdigit(notation[i+1])){num=0;return num;}if(isdigit(notation[i])&&notation[i+1]=='(' ){num=0;return num;}}for(i=0;notation[i]!='\0';i++) //约束数字的位数一共最多为七位{if(isdigit(notation[i])){m=0; //用来计数,数字的位数为7while(isdigit(notation[i])||notation[i]=='.'){i++;m++;if(notation[i]=='.'){m--;}}if(m>7){num=0;return num;}}}for(i=0;notation[i]!='\0';i++) //'('与')'需要配对存在{if(notation[i]=='(')p1++;if(notation[i]==')')p2++;if(p1!=p2){num=0;return num;}}return num;}//转换函数void shift( char notation[]){char s1[100];s1[0]='#';float s2[100][2]; //第一维放后缀表达式的元素,第二维表示小数点的位数以及是否是运算符int i=0,j=1,k=0,t=0;float sum,num1=0,num2=0; //num1为存储整数位num2为存储小数位while(notation[i]!='\0'){if(i==0&&notation[i]=='+') //第一位为正号的情况{if(isdigit(notation[++i])){num1=0; //整数部分while(isdigit(notation[i])){num1=num1*10+(notation[i]-'0'); //notation[i]-'0'可以将字符转换为整数0~9i++;}num2=0; //小数部分t=0;if(notation[i]=='.'){i++;while(isdigit(notation[i])){num2=float (num2+pow(0.1,++t)*(notation[i]-'0'));i++;}}s2[k++][0]=float(num1+num2);s2[k-1][1]=float(t);}}if(i==0&&notation[i]=='-') //第一位为负号的情况,代码与正号类似{if(isdigit(notation[++i])){num1=0;while(isdigit(notation[i])){num1=(-1)*num1*10+(-1)*(notation[i]-'0');i++;}num2=0;t=0;if(notation[i]=='.'){i++;while(isdigit(notation[i])){num2=float(num2+(-1)*pow(0.1,++t)*(notation[i]-'0'));i++;}}s2[k++][0]=float(num1+num2);s2[k-1][1]=float(t);}}if(isdigit(notation[i])) //当前字符为数字的情况与为正号的情况一样{num1=0;while(isdigit(notation[i])){num1=num1*10+(notation[i]-'0');i++;}num2=0;t=0;if(notation[i]=='.'){i++;while(isdigit(notation[i])){num2=float(num2+pow(0.1,++t)*(notation[i]-'0'));i++;}}s2[k++][0]=float(num1+num2);s2[k-1][1]=float(t);}if(notation[i]=='+'||notation[i]=='-'||notation[i]=='*'||notation[i]=='/'){ //当前的字符为操作符时,如果s1的站定为'('则将字符直接送入s1if(s1[j-1]=='('){s1[j++]=notation[i++];}}if(notation[i]=='+'||notation[i]=='-'||notation[i]=='*'||notation[i]=='/'){ //当前字符为操作符时的普通的情况if(grade(notation[i])>grade(s1[j-1])){s1[j++]=notation[i++];}else{s2[k++][0]=s1[--j];s2[k-1][1]=-1;s1[j++]=notation[i++];}}if(notation[i]=='(') //当前字符为'('的情况{s1[j++]=notation[i++];if(notation[i]=='+') //'('后跟正号的情况{if(isdigit(notation[++i])){num1=0;while(isdigit(notation[i])){num1=num1*10+(notation[i]-'0');i++;}num2=0;t=0;if(notation[i]=='.'){i++;while(isdigit(notation[i])){num2=float(num2+pow(0.1,++t)*(notation[i]-'0'));i++;}}s2[k++][0]=float(num1+num2);s2[k-1][1]=float(t);}}if(notation[i]=='-') //'('后跟负号的情况{if(isdigit(notation[++i])){num1=0;while(isdigit(notation[i])){num1=float((-1)*num1*10+(-1)*(notation[i]-'0'));i++;}num2=0;t=0;if(notation[i]=='.'){i++;while(isdigit(notation[i])){num2=float(num2+(-1)*pow(0.1,++t)*(notation[i]-'0'));i++;}}s2[k++][0]=float(num1+num2);s2[k-1][1]=float(t);}}}if(notation[i]==')') //当前字符为')'的情况{while(s1[--j]!='('){s2[k++][0]=s1[j];s2[k-1][1]=-1;}i++;}}while(j>0&&s1[--j]!='#') //依次将s1中的除了'#'外的所有操作符出栈,相当于最后的扫尾工作{s2[k++][0]=s1[j];s2[k-1][1]=-1;}printf("\n后缀表达式(逆波兰表达式):\n");display(s2,k-1);printf("\n表达式的值为:\n");sum=calculate(s2,k-1);printf("%7.4f",sum);}//计算函数float calculate(float a[][2],int k){int i,t=0,j=k;float b[100][2],c[100];for(i=k;i>=0;i--){b[i][0]=a[k-i][0];b[i][1]=a[k-i][1];}i=k;while(j>=0){if(b[i][1]!=-1){c[t]=float (b[i][0]);j--;i--;t++;}if(b[i][1]==-1) //每当遇到一个运算符则将栈最上面的两个数出栈进行运算,然后再入栈{if(int(b[i][0])=='+'){c[t-2]=float (c[t-2]+c[t-1]);}if(int(b[i][0])=='-'){c[t-2]=float (c[t-2]-c[t-1]);}if(int(b[i][0])=='*'){c[t-2]=float (c[t-2]*c[t-1]);}if(int(b[i][0])=='/'){c[t-2]= float (c[t-2]/c[t-1]);}j--;i--;t--;}}return c[0]; //运算到最后,栈中的元素即为结果}//等级函数int grade(char a) //按照运算符的优先级{if(a=='#')return 0;if(a=='(')return 1;if(a=='-'||a=='+')return 2;if(a=='*'||a=='/')return 3;if(a==')')return 4;elsereturn 5;}//显示函数void display(float a[][2],int k){int i;for(i=0;i<=k;i++){if(a[i][1]==0)printf(" %d",int(a[i][0]));if(a[i][1]==1)printf(" %7.1f",a[i][0]);if(a[i][1]==2)printf(" %7.2f",a[i][0]);if(a[i][1]==3)printf(" %7.3f",a[i][0]);if(a[i][1]==4)printf(" %7.4f",a[i][0]);if(a[i][1]==-1)printf(" %c",int (a[i][0]));}}算法实现一个表达式E的后缀形式可以如下定义:(1)如果E是一个变量或常量,则E的后缀式是E本身。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

塔里木大学信息工程学院论文编译原理课程设计课目:编译原理学生姓名:\学号:学生姓名学号:所属学院:信息工程学院班级:设计任务书指导教师(签章):年月日摘要:编译原理是计算机科学与技术专业最重要的一门专业基础课程,内容庞大,涉及面广,知识点多。

编译原理旨在介绍编译程序构造的一般原理和基本方法。

内容包括语言和文法、词法分析、语法分析、语法制导翻译、中间代码生成、存储管理、代码优化和目标代码生成。

它是计算机科学与技术专业最重要的一门专业基础课程,内容庞大,涉及面广,知识点多。

由于该课程教、学难度都非常大,往往费了大量时间而达不到预期教学效果俗语说:学习的最好方法是实践。

本课程设计正是基于此,力求为学生提供一个理论联系实际的机会,通过布置一定难度的课题,要求学生独立完成。

我们这次课程设计的主要任务是编程实现对输入合法的中缀表达式进行词法分析、语法分析,构造相应的逆波兰式,计算后缀表达式的值输出结果。

比如中缀表达式:C*(E+F),其后缀表达式为:CEF+*。

逆波兰式也叫后缀表达式,即将运算符写在操作数之后。

通过实践,建立系统设计的整体思想,锻炼编写程序、调试程序的能力,学习文档编写规范,培养独立学习、吸取他人经验、探索前言知识的习惯,树立团队协作精神。

同时,课程设计可以充分弥补课堂教学及普通实验中知识深度与广度有限的缺陷,更好地帮助学生从全局角度把握课程体系。

关键字:逆波兰式;语法分析;中缀表达式1 课设综述1.1 课设来源在通常的表达式中,二元运算符总是置于与之相关的两个运算对象之间,所以,这种表示法也称为中缀表示。

对中缀表达式的计值,并非按运算符出现的自然顺序来执行其中的各个运算,而是根据算符间的优先关系来确定运算的次序,此外,还应顾及括号规则。

因此,要从中缀表达式直接产生目标代码一般比较麻烦。

相对的,逆波兰式在计算机看来却是比较简单易懂的结构。

因为计算机普遍采用的内存结构是栈式结构,它执行先进后出的顺序。

1.2 设计意义对于实现逆波兰式算法,难度并不大,但为什么要将看似简单的中缀表达式转换为逆波兰式,原因就在于这个简单是相对人类的思维结构来说的,对计算机而言中缀表达式是非常复杂的结构。

相对的,逆波兰式在计算机看来却是比较简单易懂的结构。

因为计算机普遍采用的内存结构是栈式结构,它执行先进后出的顺序。

在逆波兰式中,不存在运算符的优先级问题,也不存在任何括号,计算的顺序完全按照运算符出现的先后次序进行。

比中缀表达式的求值要简单得多。

1.3 设计目标编写程序,实现逆波兰式的生成和计算。

首先对输入的表达式进行词法分析,然后进行语法分析,最后进行逆波兰式的输出和计算。

过实践,建立系统设计的整体思想,锻炼编写程序、调试程序的能力,学习文档编写规范,培养独立学习、吸取他人经验、探索知识的习惯。

1.4 遇到的问题如何通过递归下降方法分析表达式,并且输出词法分析、语法分析过程及结果。

如何实现把中缀表达式转换成后缀表达式,并计算表达式的结果。

1.5 需解决的关键技术本次课程设计中的关键是:通过递归下降方法分析表达式,主要有词法分析和语法分析,输出分析结果,判断表达式是否合法。

如何确定操作符的优先顺序,确定数据的进栈及出栈顺序,根据后缀表达式计算表达式的结果。

以及如何编写、调试、修改代码。

还要了解一个题目有许多种解决方法。

锻炼我们的思维能力。

2 系统分析2.1 基础知识2.1.1 词法分析基本原理词法分析程序完成的是编译第一阶段的工作。

词法分析的作用是把符流的程序变为单词序列,输出在一个中间文件上,这个文件作为语法分析程序的输入而继续编译过程。

2.1.2 递归下降的原理由于时间和技术的限制,语法分析采用递归下降的方法。

递归下降法是语法分析中最易懂的一种方法。

它的主要原理是,对每个非终极符按其产生式结构构造相应语法分析子程序,其中终极符产生匹配命令,而非终极符则产生过程调用命令。

因为文法递归相应子程序也递归,所以称这种方法为递归子程序下降法或递归下降法。

其中子程序的结构与产生式结构几乎是一致的。

2.1.3 递归下降分析的文法要求递归下降法要满足的条件:假设A的全部产生式为A→α1|α2|……|αn ,则必须满足如下条件才能保证可以唯一的选择合适的产生式predict(A→αi)∩predict(A→αj)=Φ,当i≠j。

2.1.4 后缀表达式基础知识有的编译程序直接生成目标代码,有的编译程序采用中间代码。

中间代码,也称中间语言,是复杂性介于源程序代码和机器语言的一种表示形式。

一般快速编译程序直接生成目标代码,没有中间代码翻译成目标代码的额外开销。

但是为了使编译程序在结构上更为简单明确,常采用中间代码,这样可以将与机器相关的某些实现细节置于代码生成阶段进行处理,并且可以在中间代码一级进行优化工作使得代码优化比较容易实现。

编译程序所使用的中间代码有多种形式。

我们采用的是逆波兰记号的形式。

逆波兰记号是最简单的一种中间代码表示形式,早在编译程序出现之前,它就用于表示算术表达式,是波兰逻辑学家卢卡西维奇发明的。

这种表示法将运算对象写在前面,把运算符写在后面,叫后缀表达式。

后缀表示法表示表达式,其最大优点是易于计算机处理表达式。

利用一个栈,自左向右扫描算术表达式。

每碰到运算对象,就把它推进栈,碰到运算符,若该运算符是二目的,则对栈顶部的两个运算对象实施运算,并将运算结果代替运算对象而进栈。

若是一目运算符,则对栈顶元素执行该运算,并以运算结果代替元素进栈。

最后的结果留在栈顶。

2.2 解决问题的基本思路根据课程设计的要求,程序应具有如下功能:对输入的表达式进行分析并输出逆波兰式。

由于对算数表达式的分析过程比较简单,这里采用递归下降的分析方法。

首先对输入的表达式进行词法分析,分析的结果作为语法分析的输入。

然后进行语法分析,语法分析采用递归下降的分析方法。

最后输出生成的逆波兰式并进行运算。

2.3 总体方案启动VC++,新建源程序并进行编程,程序的功能模块图如图2-1所示。

图2-1 程序功能模块图3 系统设计3.1 算法实现将一个普通的中序表达式转换为逆波兰表达式的一般算法是:1 首先构造一个运算符栈,此运算符在栈内遵循越往栈顶优先级越高的原则。

2 读入一个用中缀表示的简单算术表达式,为方便起见,设该简单算术表达式的右端多加上了优先级最低的特殊符号“#”。

3 从左至右扫描该算术表达式,从第一个字符开始判断,如果该字符是数字,则分析到该数字串的结束并将该数字串直接输出。

4 如果不是数字,该字符则是运算符,此时需比较优先关系。

做法如下:将该字符与运算符栈顶的运算符的优先关系相比较。

如果,该字符优先关系高于此运算符栈顶的运算符,则将该运算符入栈。

倘若不是的话,则将栈顶的运算符从栈中弹出,直到栈顶运算符的优先级低于当前运算符,将该字符入栈。

5 重复上述操作1-2直至扫描完整个简单算术表达式,确定所有字符都得到正确处理,我们便可以将中缀式表示的简单算术表达式转化为逆波兰表示的简单算术表达式。

转换的基本思路:1 在表达式字符串的末尾加一个代表结束的辅助符,比如“#”。

2 从头开始扫描表达式,并判断当前的每一个字符。

3 取当前的一个字符,如果当前字符是代表数字,则进逆波兰式的栈,如果是运算符,则转入4,如果是“#”,则结束。

4 比较当前运算符与临时栈中的栈顶运算符,如果栈顶运算符比当前运算符优先级高,则弹出一个运算符放进逆波兰式栈中,并继续4。

否则把当前运算符进临时栈,转入2。

图3-1 程序流程图4 代码编写在这次课程设计过程中,我主要负责的是语法分析模块,在词法分析的基础上将词法分析的结果作为语法分析的输入,语法分析采用递归下降的分析方法,部分代码如下。

int E1(){int f,t;printf("%d\tE-->TG\t",total);total++;flag=1;input();input1();f=T();if (f==0) return(0);t=G();if (t==0) return(0);else return(1);}int E(){int f,t;printf("%d\tE-->TG\t",total);total++;e[0]='E';e[1]='=';e[2]='>';e[3]='T';e[4]='G';e[5]='#';output();flag=1;input();input1();f=T();if (f==0) return(0);t=G();if (t==0) return(0);else return(1);}int T(){int f,t;printf("%d\tT-->FS\t",total);total++;e[0]='T';e[1]='=';e[2]='>';e[3]='F';e[4]='S';e[5]='#';output();flag=1;input();input1();f=F();if (f==0) return(0);t=S();if (t==0) return(0);else return(1);}int G(){int f;if(ch=='+') {b[i1]=ch;printf("%d\tG-->+TG\t",total);total++;e[0]='G';e[1]='=';e[2]='>';e[3]='+';e[4]='T';e[5]='G';e[6]='#';output();flag=0;input();input1();ch=a1[++i1];f=T();if (f==0) return(0);G();return(1);}printf("%d\tG-->^\t",total);total++;e[0]='G';e[1]='=';e[2]='>';e[3]='^';e[4]='#';output();flag=1;input();input1();return(1);}int S(){int f,t;if(ch=='*') {b[i1]=ch;printf("%d\tS-->*FS\t",total);total++;e[0]='S';e[1]='=';e[2]='>';e[3]='*';e[4]='F';e[5]='S';e[6]='#';output();flag=0;input();input1();ch=a1[++i1];f=F();if (f==0) return(0);if (t==0) return(0);else return(1);}printf("%d\tS-->^\t",total);total++;e[0]='S';e[1]='=';e[2]='>';e[3]='^';e[4]='#';output();flag=1;a1[i1]=ch;input();input1();return(1);}int F(){int f;if(ch=='(') {b[i1]=ch;printf("%d\tF-->(E)\t",total);total++;e[0]='F';e[1]='=';e[2]='>';e[3]='(';e[4]='E';e[5]=')';e[6]='#';output();flag=0;input();input1();ch=a1[++i1];f=E();if (f==0) return(0);if(ch==')') {b[i1]=ch;printf("%d\tF-->(E)\t",total);total++;flag=0;input();input1();ch=a1[++i1];}else {printf("error\n");return(0);}}else if(ch=='i') {b[i1]=ch;printf("%d\tF-->i\t",total);total++;e[0]='F';e[1]='=';e[2]='>';e[3]='i';e[4]='#';output();flag=0;input();input1();ch=a1[++i1];}else {printf("error\n");return(0);}}void yufa() /*递归分析*/{int f,p,j=0;char x;d[0]='E';d[1]='=';d[2]='>';d[3]='T';d[4]='G';d[5]='#';j=strlen(a1);a1[j]='#';n1=j;ch=b[0]=a1[0];cout<<endl<<"语法分析:"<<endl;printf("步骤\t文法\t分析串\t\t分析字符\t剩余串\n");f=E1();if (f==0) return;if (ch=='#'){printf("accept\n");p=0;x=d[p];while(x!='#') {printf("%c",x);p=p+1;x=d[p]; /*输出推导式*/}}else {printf("error\n");getchar();return;}printf("\n");getchar();}5 程序调试程序中调用了许多函数,编写代码时会出现调用的错误,使在程序运行时无法正确判断以致程序运行出错。

相关文档
最新文档