以前文章中写过一个表达式求值,但是逻辑有点乱,所以重新写了一个,该程序目前只支持个位数内的加减乘除,和以前的实现思想不一样,采用的是后缀表达式的思想,不知道的可以到网上去查查资料。觉得这样的思想,更容易理解。
通过这个程序让我收获挺大,由于需要创建两个栈,一个是数据栈,另一个是字符栈。
方案1:思想是:由于我们只支持个位数以内的加减乘除,所以对于操作数就可以使用字符表示,那么对于数据栈也可以使用字符栈来表示。
方案2:写两个栈,代码都得复制一遍,然后是修改,这样重复代码很高,这也就是引入多态的思想了,在C++/java中有多态泛型,但是C中没有,那么可以用C来实现多态,一个方案就是函数指针,另一个方案便是使用宏。本程序使用宏来实现多态。
#ifndef STACK_ELEMENT_TYPE
#error "使用本文件前,必须先定义STACK_ELEMENT_TYPE"
#endif
#ifndef STACK_TYPE
#error "使用本文件前,必须先定义STACK_TYPE"
#endif
#include
typedef struct
{
STACK_ELEMENT_TYPE* elements;
int top;
int max;
} STACK_TYPE;
int temp;
/* 注意:
* 1. 每个操作的宏,都要用#ifndef扩起,否则如果多次使用这个文件,编译器就会报告告警甚至错误
* 2. C语言的malloc返回值是void*,可以直接赋值给(stack)->elements。但是C++不行,因为C++的类型检查更加严格。
* C++可以在STACK_INIT这个宏里面再加入一个参数来表示类型,但是不推荐。C++推荐直接用模板。
**/
#ifndef STACK_INIT
#define STACK_INIT(stack, maxSize) \
((stack).elements = malloc(maxSize * sizeof(((stack).elements)[0])), (stack).top = 0, (stack).max = maxSize)
#endif
#ifndef STACK_DESTROY
#define STACK_DESTROY(stack) \
(free((stack).elements), (stack).elements = NULL, (stack).top = 0, (stack).max = 0)
#endif
#ifndef STACK_PUSH
#define STACK_PUSH(stack, value) \
(((stack).elements)[(stack).top++] = (value))
#endif
#ifndef STACK_POP
#define STACK_POP(stack, value) \
((value) = ((stack).elements)[--(stack).top])
#endif
#ifndef STACK_TOP
#define STACK_TOP(stack, value) \
( (temp)= (stack).top ,((value) = ((stack).elements)[--temp]) )
#endif
#include
#define STACK_ELEMENT_TYPE char
#define STACK_TYPE CharStack
#include "stack_template.h"
#undef STACK_ELEMENT_TYPE
#undef STACK_TYPE
#define STACK_ELEMENT_TYPE int
#define STACK_TYPE IntStack
#include "stack_template.h"
#undef STACK_ELEMENT_TYPE
#undef STACK_TYPE
typedef enum{
lparen,rparen,plus,minus,times,divide,eos,operand
} precedence;
//函数列表
int precede(char stacktop,char op);
void Postfix(const char *expr , char output[] );
int Eval(char expr[]);
precedence GetToken(char expr[], char * symbol ,int *n);
//由中缀表达式计算结果
int Eval(char postfix[])
{
char symbol;
int op1,op2;
int n=0;
precedence token=GetToken(postfix,&symbol ,&n);
IntStack intstack;
STACK_INIT(intstack, 20);
while(token !=eos)
{
if(token==operand)
STACK_PUSH(intstack, symbol-'0');
else
{
STACK_POP(intstack, op2);
STACK_POP(intstack, op1);
switch(token)
{
case plus: STACK_PUSH(intstack, op1+op2 );break;
case minus: STACK_PUSH(intstack, op1-op2 );break;
case times: STACK_PUSH(intstack, op1*op2 );break;
case divide:STACK_PUSH(intstack, op1/op2);break;
}
}
token=GetToken(postfix,&symbol,&n);
}
int result=0;
STACK_POP(intstack, result);
STACK_DESTROY(intstack);
return result;
}
precedence GetToken(char expr[], char * symbol ,int *n)
{
*symbol=expr[ (*n)++ ];
switch( *symbol)
{
case '(': return lparen;
case ')': return rparen;
case '+': return plus;
case '-': return minus;
case '*': return times;
case '/': return divide;
//case '%': return mod;
case '\0': return eos;
default : return operand;
}
}
//把中缀表达式转为后缀表达式
//不过该方法只能处理10以内的数的加减乘除,其实也可以只用一个字符栈来实现,我们输入的数据是字符串,
//所以数字也是字符表示的,这样就不必使栈支持多种数据类型.
//不过通过这个可以看出要实现多态可以采用函数指针,还有本程序采用的宏实现,或者C++里面的模板类.
/*
* 也可以不采用后缀表达式的方法实现多位数的计算。用后缀表达式不能实现多位数的运算,因为一个字符最多为255。
*/
void Postfix(const char *expr , char output[] )
{
if(!expr)
printf("the expression is null\n");
char symbol;
int i=0;
CharStack charstack;
STACK_INIT(charstack, 10);
STACK_PUSH(charstack, '#');//刚刚开始字符栈里需要一个优先级最低的字符,这样才可以比较,否则栈为空无法比较。
for( ;*expr!='\0';expr++)
{
if(*expr>='0'&& *expr <='9' )
output[i++]=*expr;
else if(*expr==')')
{
while(1)
{
STACK_POP(charstack, symbol);
if( symbol!='(')
output[i++]=symbol;
else
break;
}
}
else //此时为操作符或者lparen
{
char stacktop;
STACK_TOP(charstack, stacktop);
if( precede(stacktop, *expr)<0 )
STACK_PUSH(charstack, *expr);
else
output[i++]=*expr;
}
}
while(1) //不需要用栈判空函数,因为刚开始时默认把#字符入栈了。正好就不需要写该函数了
{
STACK_POP(charstack, symbol);
if( symbol!='#')
output[i++]=symbol;
else
break;
}
output[i]='\0';
STACK_DESTROY(charstack);
}
//比较2个操作符的优先级
int precede(char stacktop,char op)
{
int i,j;
int prior[7][7]={
{ 1, 1,-1,-1,-1, 1, 1},
{ 1, 1,-1,-1,-1, 1, 1},
{ 1, 1, 1, 1,-1, 1, 1},
{ 1, 1, 1, 1,-1, 1, 1},
{-1,-1,-1,-1,-1, 0, 2},
{ 1, 1, 1, 1, 2, 1, 1},
{-1,-1,-1,-1,-1, 2, 0}};
switch(stacktop)
{
case '+':i=0;break;
case '-':i=1;break;
case '*':i=2;break;
case '/':i=3;break;
case '(':i=4;break;
case ')':i=5;break;
case '#':i=6;break;
}
switch(op){
case '+':j=0;break;
case '-':j=1;break;
case '*':j=2;break;
case '/':j=3;break;
case '(':j=4;break;
case ')':j=5;break;
case '#':j=6;break;
}
return prior[i][j];
}
int main()
{
char expr[20]="(1*(2+3)*4)+1";
char output[20];
//scanf("%s",expr);
printf("中缀表达式为:%s\n",expr);
Postfix(expr,output);
printf("后缀表达式为:%s\n",output);
int result=Eval(output);
printf("计算结果为:%d\n",result);
return;
}