求表达式值时的栈内容图形表示
本文示范了扫描求解一个算术表达式时,操作数栈和运算符栈的情况。关于代码的主体,[bo]引用自《系统设计师教程》一书[/bo],然后参考书中代码,略作修改,使用TC绘图模式来展示两个栈在扫描过程中的动态变化情况。
写的较快因此代码略显粗糙一些。
程序代码:
/*运算表达式求值 ,我们用TC绘图形式表示操作数栈和运算符栈的情况! *代码参考了《系统设计师教程》(王春森)一书中的代码。 *by hoodlum1980 2008年10月23日 */ #include <stdio.h> #include <stdlib.h> #include <math.h> #include <graphics.h> /*和绘制相关的定义*/ #define STACKWIDTH 100 /*栈宽度*/ #define CELLHEIGHT 20 /*栈网格高度*/ #define STACKLEFT_NUM 20 /**/ #define STACKLEFT_OP (STACKLEFT_NUM + STACKWIDTH + 20) /*栈左侧坐标*/ #define STACKBOTTOM 450 /*栈底部*/ #define TEXTBOXLEFT 20 #define TEXTBOXTOP 10 #define TEXTBOXWIDTH 500 #define TEXTBOXHEIGHT 24 #define INFOBOXLEFT (TEXTBOXLEFT) /*输出文本*/ #define INFOBOXTOP (TEXTBOXTOP + TEXTBOXHEIGHT + 10) #define INFOBOXWIDTH (TEXTBOXWIDTH) #define INFOBOXHEIGHT (TEXTBOXHEIGHT) /*------------------------*/ #define POW 1 /* ^ */ #define MUL 2 /* * */ #define DIV 3 /* / */ #define ADD 4 /* + */ #define SUB 5 /* - */ #define Lp 6 /* ( */ #define Rp 7 /* ) */ #define END 8 /* \n */ #define Epsilon 1e-7 #define RADIX 10 typedef double NODE; struct { char op; /*运算符字符*/ int code; /*该码映射到运算符,使运算符像操作数那样入栈和出栈*/ } opchTbl[]= { {'*',2}, {'/',3}, {'+',4}, {'-',5}, {'(',6}, {')',7}, {'^',1}, {'=',8},{' ',-1} }; double num_stack[15]; /*操作数栈*/ char op_stack[15]; /*运算符栈*/ int numtop, optop; /*栈顶指针*/ int c = ' '; /*当前输入的字符*/ int TextPos=TEXTBOXLEFT+2; /*运算符的栈外优先级: 【^ * / + - ( )】*/ char opchars[]=" ^*/+-()="; /*有运算符组成的字符串*/ int osp[]={5,3,3,2,2,5,1,1}; /*运算符的栈内优先级*/ int isp[]={4,3,3,2,2,0}; /*我们自己的GetChar函数*/ char GetChar() { char buffer[2]; buffer[1]=0; buffer[0]=getch(); outtextxy(TextPos, TEXTBOXTOP+2, buffer); TextPos+=12; return buffer[0]; } /*显示文本*/ void DisplayInfo(char *info) { bar(INFOBOXLEFT, INFOBOXTOP, INFOBOXLEFT+INFOBOXWIDTH, INFOBOXTOP+INFOBOXHEIGHT); outtextxy(INFOBOXLEFT+2, INFOBOXTOP+INFOBOXHEIGHT/2, info); } /*绘制堆栈*/ void DrawStack(int stackleft) { int i; setcolor(WHITE); for(i=1;i<=15;i++) { bar(stackleft+1, STACKBOTTOM-i*CELLHEIGHT+1, stackleft+STACKWIDTH-1, STACKBOTTOM-(i-1)*CELLHEIGHT-1); rectangle(stackleft, STACKBOTTOM-i*CELLHEIGHT, stackleft+STACKWIDTH, STACKBOTTOM-(i-1)*CELLHEIGHT); } } void PushNum(double x) { char buffer[100]; sprintf(buffer,"%.3lf",x); setcolor(WHITE); outtextxy(STACKLEFT_NUM+5, STACKBOTTOM-numtop*CELLHEIGHT-CELLHEIGHT/2, buffer); delay(6000); num_stack[numtop++]=x; } void PushOp(char type) { char buffer[2]; buffer[0]=opchars[type]; buffer[1]=0; setcolor(WHITE); outtextxy(STACKLEFT_OP+10, STACKBOTTOM-optop*CELLHEIGHT-CELLHEIGHT/2, buffer); delay(6000); op_stack[optop++]=type; } double PopNum() { int i=numtop; if(numtop<=0) { synError(); return 0; } bar(STACKLEFT_NUM+1, STACKBOTTOM-i*CELLHEIGHT+1, STACKLEFT_NUM+STACKWIDTH-2, STACKBOTTOM-(i-1)*CELLHEIGHT-1); delay(6000); return num_stack[--numtop]; } char PopOp() { int i=optop; if(optop<=0) { synError(); return 0; } bar(STACKLEFT_OP+1, STACKBOTTOM-i*CELLHEIGHT+1, STACKLEFT_OP+STACKWIDTH-2, STACKBOTTOM-(i-1)*CELLHEIGHT-1); delay(6000); return op_stack[--optop]; } /*初始化!*/ void Init() { cleardevice(); setfillstyle(SOLID_FILL, BLACK); DrawStack(STACKLEFT_OP); DrawStack(STACKLEFT_NUM); TextPos=TEXTBOXLEFT+2; numtop=optop=0; settextjustify(LEFT_TEXT, CENTER_TEXT); } /*表达式错误*/ int synError() { bar(500,20,600,40); DisplayInfo("Input Error!"); GetChar(); closegraph(); exit(0); } /*计算操作*/ double eval(char tag, double left, double right) { int n; double result; switch(tag) { /*幂*/ case POW: for(n=1, result=left; n<right; n++) result*=left; return result; case ADD: return (left + right); case SUB: return (left - right); case MUL: return (left * right); case DIV: if(fabs(right)<=Epsilon) { DisplayInfo("divide by zero!"); exit(1); } return (left / right); } DisplayInfo("input error!"); return 1.0; } /*获取运算符,nump用于接收输入数字的指针,返回0表示接收到数字,返回其他值表示接收到运算符 */ int getToken(double *nump) { double dradix, num; int i; /*掠过空白字符*/ while(c==' ' || c=='\t') c=GetChar(); /*如果输入非数字字符*/ if(c<'0' || c>'9') { /*在运算符表中搜索该字符*/ for(i=0; opchTbl[i].code != -1 && opchTbl[i].op !=c; i++); /*是否是非法字符?*/ if(opchTbl[i].code == -1) synError(); if(c != '=') c=GetChar(); /*c获取下一个字符*/ return opchTbl[i].code; /*返回运算符的内部码*/ } num=0.0; /*跟进输入数字*/ while(c >= '0' && c <= '9') { num = RADIX * num + ( c - '0' ); c = GetChar(); } /*处理输入的小数点*/ if(c == '.') { dradix = 1.0/RADIX; /*小数单位*/ c=GetChar(); while(c >= '0' && c <= '9') { num = num + (c - '0')*dradix; dradix/=RADIX; c=GetChar(); /*跟进数字*/ } } *nump=num; return 0; /*返回0表示接收到了一个操作数,返回其他值则代表运算符*/ } /*-------------------------------*/ void main() { /*dop: 运算符, operand1:操作数1, operand2:操作数2, result:计算结果*/ double num, dop, operand1, operand2, result; int type; char ans, op, info[128]; /*ans接收用户回答是否继续时的按键*/ int gdriver=DETECT, gmode; initgraph(&gdriver, &gmode, ""); do { Init(); DisplayInfo("Please Input: "); PushOp(Lp); /* '(' 进栈*/ /*掠过前导空白字符*/ do {c=GetChar();} while(c==' ' || c=='\n' || c=='\t'); while(1) { type = getToken(&num); /*接收数字或运算符*/ if(type==0) PushNum(num); /*数字进栈*/ else { /*处理运算符,比较栈外优先级和栈内优先级*/ if( osp[type-1] > isp[ op_stack[optop-1]-1 ] ) PushOp(type); /*栈外运算符进栈*/ else { while( osp[type-1] <= isp[ op_stack[optop-1]-1 ] && op_stack[optop-1] <=5) { op=PopOp(); /*出栈运算符!*/ operand2=PopNum(); /*出栈操作数*/ operand1=PopNum(); result=eval(op, operand1, operand2); /*双目运算*/ PushNum(result); /*运算结果入栈*/ } if(type == END) break; /*一个表达式处理结束!*/ if(type == Rp) /*处理右括号')',将栈中的'('退去*/ { do {dop=PopOp();} /*退栈至'(' */ while((char)dop != Lp); } else PushOp(type); /*栈外运算符入栈*/ } } } /*计算结果出栈*/ operand1=PopNum(); sprintf(info, "result = %lf continue ? (Y/N)", operand1); DisplayInfo(info); scanf("%c", &ans); } while(ans == 'y' || ans == 'Y'); }
[[it] 本帖最后由 hoodlum1980 于 2008-10-23 03:40 编辑 [/it]]
calc_jfd.rar
(48.44 KB)