一、实验目的
将非后缀式用来表示的算术表达式转换为用逆波兰式来表示的算术表达式, 并计算用逆波兰式来表示的算术表达式的值。
二、实验原理
三、实验代码
main.cpp
#define _CRT_SECURE_NO_WARNINGS
#include"stdio.h"
#include "stdlib.h"
#include<iostream>
using namespace std;
#include <stack>
#include "OPG.h"
//逆波兰表达式变量和函数的声明
stack<float> s2; //s2作为数字栈
char arr[80] = {
0 }; //存储中缀表达式
char str1[80] = {
0 }; //存储初始后缀表达式
char str2[80] = {
0 }; //存储最终后缀表达式
int num[80] = {
0 }; //存储后缀表达式中的数字
void deal(); //由中缀生成后缀
void number(char str[]); //实现逆波兰表达式的计算
void main()
{
init();
deal();
number(str2);
}
//算符优先关系表已经有了,开始生成逆波兰式
void deal()
{
//清空缓冲区
char c;
while ((c = getchar()) != '\n' && c != EOF);
printf("请输入中缀表达式,以'#'结尾:");
gets_s(arr);
// printf("test1 arr=%s\n", arr);//测试打印arr
int i, j = 0;
stack <char>s;
s.push('#'); //初始化栈
//从左至右依次扫描字符串
for (i = 0; arr[i] != 0; i++)
{
char sym = arr[i];
if (sym >= '0' && sym <= '9')
{
int k = 0;
str1[j] = sym;
j++;
//向后看一位,如果不是数字,则加分隔符&
if (arr[i + 1] > '9' || arr[i + 1] < '0')
{
str1[j] = '&';
j++;
}
}
else
{
for (;;)
{
int x = xiabiao(s.top());
int y = xiabiao(sym);
if (dat[x][y] == '<')
{
s.push(sym);
break;
}
else
{
if (dat[x][y] == '=')
{
if (s.top() == '(' && sym == ')')
{
s.pop();
break;
}
else
break;
}
else
{
str1[j] = s.top();
j++;
s.pop();
}
}
}
}
}
//打印出初始逆波兰表达式
// printf("%s\n",str1);
//再次处理逆波兰表达式
for (i=0,k=0;str1[i]!=0;i++)
{
//如果是分隔符&,向后看一位,如果下一个不是数字,则不需要分隔符&
if(! (str1[i] == '&'&&(str1[i+1]>'9'||str1[i+1]<'0')))
{
str2[k] = str1[i];
k++;
}
}
printf("逆波兰表达式:%s\n", str2);
}
//实现逆波兰表达式的计算
void number(char str[])
{
int k = 0, p = 1;
float a = 0, b = 0, c = 0;
for (int i = 0; str[i] != 0; i++)
{
//如果是数字,将逆波兰式的数字串处理成数字
if (str[i] <= '9' && str[i] >= '0')
{
//如果下一个不是数字
if (str[i + 1] > '9' || str[i + 1] < '0')
{
num[k] = str[i] - '0';
}
//如果下一个也是数字
if (str[i + 1] <= '9' && str[i + 1] >= '0')
{
num[k] = str[i] - '0';
for (p = 1; str[i + p] != 0; p++)
{
if (str[i + p] <= '9' && str[i + p] >= '0')
{
num[k] = num[k] * 10 + str[i + p] - '0';
}
else
break;
}//k+1是数组元素个数
//相当于读入了p个字符,i需要跳过p-1
i = i + p - 1;
}
k++;
//每循环一次,得到一个数字,则数字入栈
s2.push(num[k - 1]);
}
//如果是运算符,则将栈顶两个元素弹出栈,并进入当前符号运算,将运算结果再压入数字栈
if (str[i] == '+' || str[i] == '-' || str[i] == '*' || str[i] == '/')
{
a = s2.top();
s2.pop();
b = s2.top();
s2.pop();
if (str[i] == '+')
c = b + a;
if (str[i] == '-')
c = b - a;
if (str[i] == '*')
c = b * a;
if (str[i] == '/')
c = b / a;
s2.push(c);
}
}
printf("逆波兰式%s=计算结果为%.2f\n", str2, c);
}
OPG.h
//算符优先文法全局变量和函数的声明
char dat[20][20];//算符优先关系
char s[200];//模拟符号栈
char lable[20];//文法终极符集
char input[100];//文法输入符号串
char str[20][10];//用于输入串的分析
int k;
char a;
int j;
char q;
int r;//文法规则个数
int r1;
int m, n, N;//转化后文法规则个数
char st[10][30];//用来存储文法规则
char first[10][10];//文法非终结符FIRSTVT集
char last[10][10];//文法非终结符LASTVT集
int fflag[10] = {
0 };//标志第i个非终结符的FIRSTVT集是否已求出
int lflag[10] = {
0 };//标志第i个非终结符的LASTVT集是否已求出
int zhongjie(char c);//判断字符c是否是终结符
int xiabiao(char c);//求字符c在算符优先关系表中的下标
void firstvt(char c);//求非终结符c的FIRSTVT集
void lastvt(char c);//求非终结符c的LASTVT集
void table();//创建文法优先关系表
void init();
void init()
{
int i, j, k = 0;
printf("请输入文法规则数:");
scanf("%d", &r);
printf("请输入文法规则:\n");
for (i = 0; i < r; i++)
{
scanf("%s", st[i]);//存储文法规则,初始化FIRSTVT集和LASTVT集
first[i][0] = 0;//first[i][0]和last[i][0]分别表示st[i][0]非终结符的FIRSTVT集和LASTVT集中元素的个数
last[i][0] = 0;
}
//判断文法是否合法
for (i = 0; i < r; i++)
{
for (j = 0; st[i][j] != '\0'; j++)
{
if (st[i][0] < 'A' || st[i][0]>'Z')
{
printf("不是算符文法!\n");
exit(-1);
}
if (st[i][j] >= 'A' && st[i][j] <= 'Z')
{
if (st[i][j + 1] >= 'A' && st[i][j + 1] <= 'Z')
{
printf("不是算符文法!\n");
exit(-1);
}
}
}
}
for (i = 0; i < r; i++)
{
for (j = 0; st[i][j] != '\0'; j++)
if ((st[i][j] < 'A' || st[i][j]>'Z') && st[i][j] != '=' && st[i][j] != '>' && st[i][j] != '|')
lable[k++] = st[i][j];
}
lable[k] = '#';
lable[k + 1] = '\0';
table();
}
void table()
{
char text[20][10] = {
0 };
int i, j, k, t, l, x = 0, y = 0;
int m, n;
x = 0;
for (i = 0; i < r; i++)
{
firstvt(st[i][0]);
lastvt(st[i][0]);
}
//文法转化
for (i = 0; i < r; i++)
{
text[x][y] = st[i][0];
y++;
for (j = 1; st[i][j] != '\0'; j++)
{
if (st[i][j] == '|')
{
text[x][y] = '\0';
x++;
y = 0;
text[x][y] = st[i][0];
y++;
text[x][y++] = '-';
text[x][y++] = '>';
}
else
{
text[x][y] = st[i][j];
y++;
}
}
text[x][y] = '\0';
x++;
y = 0;
}
r1 = x;
for (i = 0; i < x; i++)//求每个终结符的推导结果(去掉"->"后的转化文法,用于最后的规约)
{
str[i][0] = text[i][0];
for (j = 3, l = 1; text[i][j] != '\0'; j++, l++)
str[i][l] = text[i][j];
str[i][l] = '\0';
}
for (i = 0; i < x; i++)
{
for (j = 1; text[i][j + 1] != '\0'; j++)
{
if (zhongjie(text[i][j]) && zhongjie(text[i][j + 1]))
{
m = xiabiao(text[i][j]);
n = xiabiao(text[i][j + 1]);
dat[m][n] = '=';
}
if (text[i][j + 2] != '\0' && zhongjie(text[i][j]) && zhongjie(text[i][j + 2]) && !zhongjie(text[i][j + 1]))
{
m = xiabiao(text[i][j]);
n = xiabiao(text[i][j + 2]);
dat[m][n] = '=';
}
if (zhongjie(text[i][j]) && !zhongjie(text[i][j + 1]))
{
for (k = 0; k < r; k++)
{
if (st[k][0] == text[i][j + 1])
break;
}
m = xiabiao(text[i][j]);
for (t = 0; t < first[k][0]; t++)
{
n = xiabiao(first[k][t + 1]);
dat[m][n] = '<';
}
}
if (!zhongjie(text[i][j]) && zhongjie(text[i][j + 1]))
{
for (k = 0; k < r; k++)
{
if (st[k][0] == text[i][j])
break;
}
n = xiabiao(text[i][j + 1]);
for (t = 0; t < last[k][0]; t++)
{
m = xiabiao(last[k][t + 1]);
dat[m][n] = '>';
}
}
}
}
m = xiabiao('#');
for (t = 0; t < first[0][0]; t++)
{
n = xiabiao(first[0][t + 1]);
dat[m][n] = '<';
}
n = xiabiao('#');
for (t = 0; t < last[0][0]; t++)
{
m = xiabiao(last[0][t + 1]);
dat[m][n] = '>';
}
dat[n][n] = '=';
}
void firstvt(char c)//求firstvt集
{
int i, j, k, m, n;
for (i = 0; i < r; i++)
{
if (st[i][0] == c)
break;
}
if (fflag[i] == 0)
{
n = first[i][0] + 1;
m = 0;
do {
if (m == 2 || st[i][m] == '|')
{
if (zhongjie(st[i][m + 1]))
{
first[i][n] = st[i][m + 1];
n++;
}
else
{
if (zhongjie(st[i][m + 2]))
{
first[i][n] = st[i][m + 2];
n++;
}
if (st[i][m + 1] != c)
{
firstvt(st[i][m + 1]);
for (j = 0; j < r; j++)
{
if (st[j][0] == st[i][m + 1])
break;
}
for (k = 0; k < first[j][0]; k++)
{
int t;
for (t = 0; t < n; t++)
{
if (first[i][t] == first[j][k + 1])
break;
}
if (t == n)
{
first[i][n] = first[j][k + 1];
n++;
}
}
}
}
}
m++;
} while (st[i][m] != '\0');
first[i][n] = '\0';
first[i][0] = --n;
fflag[i] = 1;
}
}
void lastvt(char c)//求LASTVT集
{
int i, j, k, m, n;
for (i = 0; i < r; i++)
{
if (st[i][0] == c)
break;
}
if (lflag[i] == 0)
{
n = last[i][0] + 1;
m = 0;
do {
if (st[i][m + 1] == '\0' || st[i][m + 1] == '|')
{
if (zhongjie(st[i][m]))
{
last[i][n] = st[i][m];
n++;
}
else
{
if (zhongjie(st[i][m - 1]))
{
last[i][n] = st[i][m - 1];
n++;
}
if (st[i][m] != c)
{
lastvt(st[i][m]);
for (j = 0; j < r; j++)
{
if (st[j][0] == st[i][m])
break;
}
for (k = 0; k < last[j][0]; k++)
{
int t;
for (t = 0; t < n; t++)
{
if (last[i][t] == last[j][k + 1])
break;
}
if (t == n)
{
last[i][n] = last[j][k + 1];
n++;
}
}
}
}
}
m++;
} while (st[i][m] != '\0');
last[i][n] = '\0';
last[i][0] = --n;
lflag[i] = 1;
}
}
int xiabiao(char c)//求字符c在算符优先关系表中的下标
{
int i;
for (i = 0; lable[i] != '\0'; i++)
{
if (c == lable[i])
return i;
}
return -1;
}
int zhongjie(char c)//判断字符c是否是终结符
{
int i;
for (i = 0; lable[i] != '\0'; i++)
{
if (c == lable[i])
return 1;
}
return 0;
}
四、实验总结
1、数据结构:在生成逆波兰表达式时,主要用了符号栈;在计算逆波兰表达式时,主要用了数字栈。
2、本实验是算符优先文法的一个应用,所以我直接将中缀表达式对应的算符文法的优先分析表拿过来用。
该文法能识别带有+,-,,/,(,)的中缀表达式。
E=>E+T|E-T|T
T=>TP|T/P|P
P=>(E)|i
(注:由于涉及减法” - ”的运算,所以->改为=>)
得到算符优先关系后,用其限定符号栈(栈顶符号优先级最高)。
3、在生成逆波兰表达式时,在deal( )函数中,需要体现数字串,通过“向后看一位,如果不是数字,则加分隔符&”,可以在每个数字串后面跟上一个分隔符,再通过“如果是分隔符&,向后看一位,如果下一个不是数字,则不需要分隔符&”,可以实现只在连续数字之间有分隔符;在计算逆波兰表达式时,在number( )函数中,从左向右读入逆波兰表达式,通过如“num[k] = str[i] - ‘0’;”实现字符转化为数字,然后将数字符号串转化为数字。
4、清空输入缓冲区
//清空缓冲区
char c;
while ((c = getchar()) != ‘\n’ && c != EOF);
5、待改进:本实验尚未实现一目运算符如负号的运算;遇到错误没有尽可能地显示错误信息。