功能:例如输入字符串“1+(2*3)/4+5”,输出结果“7.25”
局限:因为写得比较简单,还没有添加字符串是否合法的检测,仅支持加减乘除运算
思路:找到字符串中优先级最低的运算符,将字符串从这里分割成两个算式,分别对分割后的算式进行递归求值。递归出口为字符串中不含有运算符,这时候可以直接用字符串转数值函数取得数值。
难点在于对运算符优先级的判断。
显然,加减的级别比乘除低,不妨设加减为1,乘除为2作为基本的值,但是运算符的优先级在算式中通常会有各种变化,我们来总结一下各种变化的规则:
1、从左到右计算
2、先算乘除,再算加减加减
3、有括号先算括号里面
针对以上规则我们可以这样解决:
1、从左到右给各运算符加一个逐渐递减的增量,即从右到左给各运算符加一个逐渐递增的增量,例如算式1/2*3,设*号优先级为2.0,则/号优先级为2.1。
2、将加减设为1,乘除设为2 。
第一条中从左到右的增量不能太大以至于加减号的优先级高于乘除号
3、给括号内的运算符优先级加上一个较大的增量(应使加减号加上增量后能优先于乘除号),例如算式1/(2+3),+号优先级为11,/号优先级为2。。在知道算式中每个符号的优先级后,即使将括号去除,我们也能得到正确得结果
例如:即使将1*(2+3)/4
变为1*2+3/4,其中
*
优先级
2.1
+ 优先级
11
/
优先级
2.0
我们可以把算式按照最初的思路分割成
算式:1*2+3
运算符:/
算式:4
算式:1*2+3
再分割成:
算式:1
运算符:*
算式
:2+3
算式:2+3
再分割成:
算式:2
运算符:+
算式 :3
至此,所有的值就都能求出来了。
程序说明:
为此我们需要以下结构和函数:
1、保存算式和算式中运算符的优先级的结构体
C++ Code
1
2
3
4
5
6
|
|
struct opPair //将原先的算式字符串封装转换成成字符和优先级一一对应的形式,通过strTo函数转换
{
char str[40]; //存储算式
float prior[40]; //存储每个字符对应的优先级,其中数字位置的优先级全置为最高,不影响比较
};
|
例如:str="1+2*3"
则: prior[0]=100
prior[1]=1
prior[2]=100
prior[3]=2
prior[4]=100
2、将输入的算式字符串转换成opPair的函数,即计算每个运算符优先级的函数strTo()
C++ Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
|
opPair strTo(char* str) //将原始的算式转换成带有运算符优先级的形式,即opPair结构体
{
int len=strlen(str);
opPair pair;
for(int i=0;i<</span>40;i++) //初始时全部置为最高
{
pair.prior[i]=100;
}
int level=0; //进出括号时的优先级调整
int cnt=0;
float offAdd=0; //不同顺序的加减号的优先级调整
float offMul=0; //不同顺序的乘除号的优先级调整
for (int i=0;i//遍历算式
{
if(str[i]=='(') //进入括号时,level加一个较高的数 ,表示括号里的运算符有更高的优先级
{
level+=10;
}
if(str[i]==')') //出括号时,level减去这个数
{
level-=10;
}
//经过level的调整后,算式中的括号已无意义,if 当前字符是括号,跳过,不将str中的括号存储到opPair.str中
if(str[i]!='('&&str[i]!=')')
{
if(isOpe(str[i]))
{
pair.prior[cnt]=prior(str[i])+level;
}
pair.str[cnt]=str[i];
cnt++;
}
}
pair.str[cnt]='\0';
//对opPair.str倒序遍历,调整因顺序引起的加减号和乘除号的优先级变化 ,给顺序在前的符号加一个小的增量
for (int i=len-1;i>=0;i--)
{
if(pair.str[i]=='+'||pair.str[i]=='-')
{
pair.prior[i]+=offAdd;
offAdd+=0.1;
}
if(pair.str[i]=='*'||pair.str[i]=='/')
{
pair.prior[i]+=offMul;
offMul+=0.1;
}
}
// for (int i=0;i<10;i++)
// printf("%.2f ",pair.prior[i]);
// printf("\n"); return pair;
}
|
3、算式分割函数,split()
C++ Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
|
opPair* split(opPair str) //分割一个算式
{
//qel用于保存分割后的字符串,
//其中eql[0]保存前一个算式,eql[1]保存后一个算式,eql[2]保存两个算式之间的运算符
opPair* eql=new opPair[3];
int flag=0; //用于记录运算符的位置
char ope='M'; //用于保存运算符的值,初始时为最高优先级的运算符
float level=100;
for (int i=0;i
{
if(isOpe(str.str[i]))
{
if(str.prior[i]
{
ope=str.str[i];
level=str.prior[i];
flag=i;
}
}
}
//以下为找到分割位置后对eql的赋值
int i=0;
for(i=0;i
{
eql[0].str[i]=str.str[i];
eql[0].prior[i]=str.prior[i];
}
eql[0].str[i]='\0';
i++;
int j=0;
for(j=0;j1;j++)
{
eql[1].str[j]=str.str[i];
eql[1].prior[j]=str.prior[i];
i++;
}
eql[1].str[j]='\0';
eql[2].str[0]=ope;
eql[2].str[1]='\0';
// printf("ope=%c flag=%d\n",ope,flag);
// printf("%s\n",eql[0].str);
// for(int i=0;i<10;i++){
// printf("%d ",eql[0].prior[i]);
// }
// printf("\n");
// printf("%s\n",eql[1].str);
// for(int i=0;i<10;i++){
// printf("%d ",eql[1].prior[i]);
// }
// printf("\n");
// printf("%s\n\n\n",eql[2].str);
return eql;
}
|
4、递归求值函数cal()
C++ Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
|
float cal(opPair str)
{
bool flag=true; //用于记录字符串中是否含有运算符的标志
for(int i=0;i
{
if(isOpe(str.str[i]))
{
flag=false;
break;
}
}
if (flag==false) //若含有运算符,则进行分割递归
{
opPair* eql=split(str);
opPair eql0=eql[0];
opPair eql1=eql[1];
opPair eql2=eql[2];
// printf("%s\n",eql0.str);
// printf("%s\n",eql1.str);
// printf("%s\n",eql2.str);
// printf("\n");
delete[] eql;
switch(eql[2].str[0])
{
case '+':return cal(eql0)+cal(eql1);
case '-':return cal(eql0)-cal(eql1);
case '*':return cal(eql0)*cal(eql1);
case '/':return cal(eql0)/cal(eql1);
}
}
else //若不含有运算符,则直接求值
{
if(str.str=="\0")
{
return 0;
}
else{
int tmp =atof(str.str);
// printf("tmp = %d\n",tmp);
return tmp;
}
}
}
|
代码(需添加以上的函数和结构体):
C++ Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
|
#include #include #include #include
int prior(char a) //返回运算符的优先级
{
switch(a)
{
case '+':return 1;
case '-':return 1;
case '*':return 2;
case '/':return 2;
case 'M':return 100; //实际没有此运算符,仅为方便运算符比较
case 'm':return -1; //同上
}
return 0;
}
bool isNum(char a) //字符a是否是数字字符
{
if(a<='9'&&a>='0')
return true;
else
return false;
}
bool isOpe(char a) //字符a是否是运算符
{
if(a=='+'||a=='-'||a=='*'||a=='/')
return true;
else
return false;
}
float Cal(char* str)
{
return cal(strTo(str));
}
int main()
{
char str[50];
gets(str);
while(strcmp(str,"exit")!=0)
{
printf("%f",Cal(str));
printf("\n\n");
gets(str);
}
}
|
运行截图:
感谢参考,有失误不足之处还请谅解指正。
加载中,请稍候......