目录
1 题目的内容及要求 (2)
2 需求分析 (2)
3 概要设计 (2)
4 详细设计 (4)
5 源代码 (6)
6 运行结果及分析 (13)
7 收获及体会 (14)
1 题目的内容及要求
从文件读取表达式,判断表达式是否合理,将表达式转换成后缀形式,按后缀表达式求值;题目涉及加减乘除,带括弧的混合运算;随时可以退出。
2 需求分析
利用栈设计一个程序,该程序能够用于表达式求值,程序将读入的中缀表达式转换为后缀表达式,然后读取后缀表达式,输出结果。
本程序具有检测表达式语法是否正确的功能,如果表达式出现错误的时候,程序会提示操作人员执行的表达式不合理,语法是不能执行的。语法正常的情况下,程序可以实现了加、减、乘、除以及带括号的基本运算。
程序在执行表达式的时候,先检查表达式是否合理,不合理则输出表达式不符合要求,合理则将中缀表达式转化为后缀表达式,然后则对表达式求值,输出结果。
3 概要设计
本程序选用的是线性表数据结构。它按照后进先出的原则存储数据,先进
的数据被压入栈底,最后的数据在栈顶,需要度数据的时候才栈顶开式探出数据。
程序采用的是顺序存储结构,可以将逻辑上相邻的数据元素在物力上相邻的存储单元里,节电之间的关系由存储单元相邻的关系来决定。
选择线性表结构是因为程序是用栈来设计的,可以将优先运算的送至栈顶,低级别的运算则最后根据先后进栈的顺序来执行。选择顺序存储结构是因为顺序存储结构存取数据数度快,占用的存储空间小。
系统的功能模块划分:
translate()的功能是将中缀表达式转换为后缀表达式
disppostfix()的功能是输出后缀表达式
processexpress()的功能是将原表达式进行预处理,检查符号是否匹配,转化为中缀式。
endright的功能是先对表示式的运算符进行处理,考虑其计算的优先级。
findsymbol()的功能是对表达式的括号进行优先处理。
findword()的功能是检查表达式是否有语法错误。
操作之间的调用关系:
各个函数是通过主函数main()来调用的,当程序接受一段表达式的时候,先通过processexpress()对整个表达式做一个运算的预处理,转化为中缀式。findword()检查表达式是否出现可以执行,然后送入findsymbol()进行处理,带括号的运算优先处理,之后endright函数检查表达式的优先级,高级的运算先进行处理。接着translate()函数把表达式转换为后缀式。disppostfix()的功能是输出后缀表达式。计算表达式。最后主函数输出计算结果。
系统流程图:
开始
输入表达式
表达式是否合法
将表达式转换为后缀式
计算
输出计算结果
结束
4 详细设计
在计算机中,算数表达式的计算通常是通过使用栈来实现的。所以表达式求值程序最主要的数据结构就是栈。可以使用栈来存储输入表达式的操作符和操作数。输入的表达式是由操作数和操作符以及改变运算次序的括号连接而成的。
(1)本程序通过disppostfix()的功能是输出后缀表达式。
将中缀式转化为后缀式,要将遇到的运算对象直接放入后缀式的存储区。将刚读入的字符送至操作数栈,如果遇到运算符则送入运算符栈。通过栈的先后进栈的顺序来将操作数和操作符进行出栈,然后输出后缀表达式。
void disppostfix()
{ int i;
printf(" 后缀表达式:");
for (i=0;i<=postlength;i )
{ if (strcmp(lexicon[postfix[i]].name,"number")==0)
printf("%g ",lexicon[postfix[i]].info.val);
else
printf("%s ",lexicon[postfix[i]].name); } }
(2)程序通过findsymbol()的功能是对表达式的括号进行优先处理。
该函数使用栈的方法,来解决表达式中带括号的进行先处理,当两个栈都处于一个优先级的是时候,说明表达式出现左右括号,进行优先运算。对表达式的括号优先处理。int findsymbol(char str[],int pos)
{ int h,k;
char word[maxtoken];
word[0]=str[pos];
word[1]='\0';
pos ;
if ((h=hashtable[hash(word)])==-1)
{ printf("表达式中存在不能识别的符号\n");
return -1; }
else if (leading()==1)
{ if (kind(h)==rightparen)
{ printf("不应为右括号\n");
return -1; }
else if (kind(h)!=binaryop)
puttoken(h);
else{ if (strcmp(word," ")==0);
else if (strcmp(word,"-")==0)
puttoken(hashtable[hash("~")]);
else{ printf(" >>二元运算符不正确\n");
return -1; } } }
else{ if (kind(h)==binaryop || kind(h)==rightparen)
puttoken(h);
else { printf("二元运算符不正确\n");
return -1; } }
if ((k=kind(h))==leftparen)
parencount ;
else if (k==rightparen)
if (--parencount<0)
{ printf("太多的右括号\n");
return -1;}
return pos;}
(3)findword()的功能是检查表达式是否有语法错误。
通过主函数调用后,对表达式进行语法差错。当语法出现错误时,则通过return()=0,f返回到主函数,报告表达式出错。输出结果,表达式出错。当表达式是合理的时候,该函数运行结束,则停止调用,进入主函数。实现下一个函数的调用。
int findword(char str[],int pos)
{ int h;
char word[maxtoken];
pos=extractword(str,pos,word);
h=hashtable[hash(word)];
if (h!=-1)
{ if (leading()==1)
{ if (kind(h)==binaryop)
{
printf("二元运算符位置不正确\n");
return -1; }
else
puttoken(h);}
else{ if (kind(h)!=binaryop)
{ printf("应为二元运算符\n");
return -1; }
else
puttoken(h); }
return pos; }
else { printf(" >>不正确的标识符\n");
return -1; } }
int findnumber(char str[],int pos)
{ if (leading()==0)
{ printf("常量位置不正确\n");
return -1;}
else{ lexicon[ tokencount].kind=operand;
lexicon[tokencount].info.val=atof(&str[pos]);
strcpy(lexicon[tokencount].name,"number");
puttoken(tokencount);
for (;isdigit(str[pos]) || str[pos]=='.';pos );
return pos; } }
5 源代码
#include
#include
#include
#include
#define maxname 7
#define maxpriority 6
#define maxtoken 100
#define maxstack 100
#define maxstring 101
#define hashsize 101
#define lastoperand 17
typedef double value_type;
typedef enum kind_tag {unaryop,binaryop,operand,leftparen,rightparen,endexpr} kind_type; typedef struct
{ char name[maxname];
kind_type kind;
union
{ int pri;
value_type val;
} info;
} token_type;
token_type lexicon[maxtoken]={
{"#",endexpr},
{"(",leftparen},
{")",rightparen},
{"~",unaryop,6},
{"abs",unaryop,6},
{"sqrt",unaryop,6},
{"exp",unaryop,6},
{"ln",unaryop,6},
{"log10",unaryop,6},
{"sin",unaryop,6},
{"cos",unaryop,6},
{"tanh",unaryop,6},
{" ",binaryop,4},
{"-",binaryop,4},
{"*",binaryop,5},
{"/",binaryop,5},
{"%",binaryop,5},
{"^",binaryop,6}};
int hashtable[maxtoken];
int infix[maxtoken];
int postfix[maxtoken];
int inlength;
int postlength;
int parencount;
int tokencount;
int hash(char *name)
{ int h=name[0] % hashsize;
while (1)
{ if (hashtable[h]==-1)
break;
else if (strcmp(lexicon[hashtable[h]].name,name)==0) break;
else
{ if (name[1]=='\0')
h =31;
else
h =name[1];
h%=hashsize; } }
return abs(h); }
void makehashtable()
{ int i;
for (i=0;i
hashtable[i]=-1;
for (i=1;i<=lastoperand;i )
hashtable[hash(lexicon[i].name)]=i; }
kind_type kind(int h)
{ return(lexicon[h].kind); }
int priority(int h)
{ return(lexicon[h].info.pri); }
int leading()
{ int k;
if (inlength<=-1)
return 1;
else
return (k=kind(infix[inlength]))==leftparen || k==unaryop || k==binaryop; } void puttoken(int h)
{ inlength ;
infix[inlength]=h; }
void puttoken1(int h)
{ postlength ;
postfix[postlength]=h; }
int extractword(char str[],int pos,char *word)
{ int i;
char *pw=word;
for (i=pos;isalpha(str[i]) || isdigit(str[i]);i )
*pw =tolower(str[i]);
*pw='\0';
return i; }
int findword(char str[],int pos)
{ int h;
char word[maxtoken];
pos=extractword(str,pos,word);
h=hashtable[hash(word)];
if (h!=-1)
{ if (leading()==1)
{ if (kind(h)==binaryop)
{
printf("二元运算符位置不正确\n");
return -1; }
else
puttoken(h);}
else{ if (kind(h)!=binaryop)
{ printf("应为二元运算符\n");
return -1; }
else
puttoken(h); }
return pos; }
else { printf(" >>不正确的标识符\n");
return -1; } }
int findnumber(char str[],int pos)
{ if (leading()==0)
{ printf("常量位置不正确\n");
return -1;}
else{ lexicon[ tokencount].kind=operand;
lexicon[tokencount].info.val=atof(&str[pos]);
strcpy(lexicon[tokencount].name,"number"); puttoken(tokencount);
for (;isdigit(str[pos]) || str[pos]=='.';pos );
return pos; } }
int findsymbol(char str[],int pos)
{ int h,k;
char word[maxtoken];
word[0]=str[pos];
word[1]='\0';
pos ;
if ((h=hashtable[hash(word)])==-1)
{ printf("表达式中存在不能识别的符号\n");
return -1; }
else if (leading()==1)
{ if (kind(h)==rightparen)
{ printf("不应为右括号\n");
return -1; }
else if (kind(h)!=binaryop)
puttoken(h);
else{ if (strcmp(word," ")==0);
else if (strcmp(word,"-")==0)
puttoken(hashtable[hash("~")]);
else{ printf(" >>二元运算符不正确\n");
return -1; } } }
else{ if (kind(h)==binaryop || kind(h)==rightparen) puttoken(h);
else { printf("二元运算符不正确\n");
return -1; } }
if ((k=kind(h))==leftparen)
parencount ;
else if (k==rightparen)
if (--parencount<0)
{ printf("太多的右括号\n");
return -1;}
return pos;}
void gettoken(int &h)
{ inlength ;
h=infix[inlength];}
void gettoken1(int &h)
{ postlength ;
h=postfix[postlength];
} void translate()
{ int st[maxstack];int top=-1;
int h,h1;
kind_type type;
postlength=-1;
inlength=-1;
int endright;
do{ gettoken(h);
switch(type=kind(h))
{ case operand: puttoken1(h); break;
case leftparen: top ; st[top]=h; break;
case rightparen: h=st[top];top--;
while (top>-1 && kind(h)!=leftparen)
{puttoken1(h);
h=st[top];top--; }break;
case unaryop:
case binaryop: endright=0;
do{ if (top==-1)
endright=1;
else if (kind(st[top])==leftparen)
endright=1;
else if (priority(st[top])
endright=1;
else if (priority(st[top])==priority(h) && priority(h)==maxpriority) endright=1;
else{ h1=h;
endright=0;
h=st[top];top--;
puttoken1(h);
h=h1; } } while (endright==0);
top ;st[top]=h;
break;
case endexpr: while (top>-1)
{ h=st[top];top--;
puttoken1(h); }
break; } } while (type!=endexpr); puttoken1(0); }
int processexpress(char *instring)
{ int len,pos;
inlength=-1;
parencount=0;
tokencount=lastoperand;
len=strlen(instring);
instring[len]='\0';
for (pos=0;pos
{ if (instring[pos]==' ') pos ;
else if (isalpha(instring[pos]))
pos=findword(instring,pos);
else if (isdigit(instring[pos]) || instring[pos]=='.')
pos=findnumber(instring,pos);
else pos=findsymbol(instring,pos);
if (pos==-1)
return 0; }
if (parencount!=0)
printf("左右括号不匹配\n");
puttoken(0);
return 1; }
void disppostfix()
{ int i;
printf(" 后缀表达式:");
for (i=0;i<=postlength;i )
{ if (strcmp(lexicon[postfix[i]].name,"number")==0) printf("%g ",lexicon[postfix[i]].info.val);
else
printf("%s ",lexicon[postfix[i]].name); } }
value_type dobinary(int h,value_type x,value_type y) { switch(h) { case 12: return(x y);
case 13: return(x-y);
case 14: return(x*y);
case 15: if (y!=(value_type)0)
return(x/y);
else{ printf(" >>除零错误\n"); break; }
case 16: if (y!=(value_type)0)
return(fmod(x,y));
else{ printf(" >>除零错误\n"); break; }
case 17: return(pow(x,y));
default: printf(" >>%d是无效的二元运算符\n",h); break; } }
value_type dounary(int h,value_type x)
{ switch(h) { case 3: return(-x);
case 4:return(abs(x));
case 5: if (x>=0)
return(sqrt(x));
else{ printf(" >>负数不能开平方\n");
break; }
case 6: return(exp(x));
case 7: if (x>0)
return(log(x));
else
{ printf(" >>负数不能求ln\n");
break; }
case 8: if (x>0)
return(log10(x));
else{printf(" >>负数不能求log10\n"); break;}
case 9: return(sin(x));
case 10: return(cos(x));
case 11: return(tanh(x)); } }
value_type getvalue(int h)
{ if (kind(h)!=operand)
printf(" >>不是一个操作数\n");
else
return(lexicon[h].info.val);}
value_type evaluatepostfix()
{ kind_type type;
int h;
value_type x,y;
double st[maxstack];
int top=-1;
postlength=-1;
do { gettoken1(h);
switch(type=kind(h))
{
case operand: top ;
st[top]=getvalue(h); break;
case unaryop: x=st[top];
top--;top ;
st[top]=dounary(h,x);break;
case binaryop: y=st[top];top--;
x=st[top];top--;
top ; st[top]=dobinary(h,x,y);break;
case endexpr: x=st[top];top--;
if (top>-1) printf(" >>不正确的表达式\n"); break;}
} while (type!=endexpr);
return(x); };
void main()
{ char instring[maxstring];
makehashtable();
printf("\n");
printf(" 输入一个表达式:");
gets(instring);
while (strlen(instring)!=0)
{ if (processexpress(instring))
{ translate(); disppostfix();
printf(" 运算结果:%g\n\n",evaluatepostfix()); }
printf(" 输入一个表达式:");
gets(instring); }
printf("\n"); } }
6 运行结果及分析
输入的表达式是合理的时候,程序会计算出结果并给出转化后的后缀式。
当输入的表达式出现问题时,程序则会提示表达式中出现不能识别的符号。
当表达式缺少括号的时候,程序则会提示左右括号不匹配,同时给出缺少括号时的结果
7 收获及体会
在设计表达式的值的时候,要先做好程序的算法,先要实现算法,做好具体分析,通过所给定的内容来设计程序,利用算法来简化我们的过程,也同时可以避免一些不必要发错误。
在设计程序中遇到许多的问题,我学会了在探索中学习知识,数据结构的课程设计,要求我们要在设计题目中有一个清晰的头脑,在设计程序时,要有一个明确的思路,对程序要有一个大概的框架。
我们要学会合理的利用网络资源,在遇到困难的时候与同学之间相互讨论来解问题。在设计题目时遇到的问题及时解决,及时解决问题,不要将问题堆积在一起才可以更好的解决问题。