时隔几年,补上之前四则运算计算器的简单注释
基本原理是:按优先级分解算式字符串
在分解加减运算时,以不在括号里的'+', '-'为分隔,分解字符串,返回计算结果
block0 = block1 +(-) block2 +(-) block3
再对每个block分解乘除运算,以不在括号里的'+', '-'为分隔,分解字符串,返回计算结果
block3 = block4 *(/) block5 *(/) block6
再对每个子block区分括号和数值
- 对于括号(字符串以左括号开头):将括号内的字符串作为新的算式字符串,迭代计算该字符串,返回计算结果
- 对于数值(字符串不左括号开头):将数值字符串转换为数值,返回数值
对于加减或乘除分解(这里以加减为例),每次提取出最右的一个block,再迭代分解左边的部分
block0 = [ block1 ... block4 ] +(-) block5
[ block1 ... block4 ] = [ block1 ... block3 ] +(-) block4
...
至于为什么是从右边分解,因为加减(或乘除)运算是从左往右计算的,而在迭代的过程中,优先计算的是后迭代的内容
- A - B + C = (A - B) + C
- A - B + C ≠ A - (B + C)
新注释过的代码
#include <stdio.h>
#define MAX_STR 10000
// 自己实现的 strlen
char *highofstr(char *low)
{
while(*low!='\0')
low++;
return low-1;
}
// 把数值字符串转换为数值
double trans(char *low, char *high)
{
double num=0;
int cnt_point=-1;
while(low<=high)
{
if( *low=='.' || cnt_point>-1 )
cnt_point++;
if( *low!='.' )
num= 10*num+((*low)-'0');
low++;
}
for( ; cnt_point>0; cnt_point--)
num /=10;
return num;
}
// 标记字符
// 数字、小数点 : 9
// +、- : 1
// *、/ : 2
// (、) : 迭代计算括号内字符串,结果视为数值,标记为9
int flagch(char ch)
{
if('0'<=ch && ch<='9')return 9;
if(ch=='.')return 9;
if(ch=='+' || ch=='-')return 1;
if(ch=='*' || ch=='/')return 2;
if(ch=='(' || ch==')')return 9;
return -1;
}
double deal_1(char *low, char *high);
double deal_2(char *low, char *high);
double deal_9(char *low, char *high);
// 优先级1(最低) 计算 +- :
// 从右往左找括号栈为0的第一个+或-
// 计算 左 (+或-) 右
// 左 : 可能有括号外的+-,可能有括号外的*/,可能有()
// 右 : 没有括号外的+-,可能有括号外的*/,可能有()
double deal_1(char *low, char *high)
{
int cnt_kh=0;
char *p=high;
while( (flagch(*p)!=1 && p>low) || (cnt_kh!=0) )
{
if(*p=='(')cnt_kh++;
if(*p==')')cnt_kh--;
p--;
}
if(p<=low)
return deal_2(low, high);
else if(*p=='+')
return deal_1(low, p-1)+deal_2(p+1, high);
else if(*p=='-')
return deal_1(low, p-1)-deal_2(p+1, high);
}
// 优先级2 计算 */ :
// 从右往左找括号栈为0的第一个*或/
// 计算 左 (*或/) 右
// 左 : 没有括号外的+-,可能有括号外的*/,可能有()
// 右 : 没有括号外的+-,没有括号外的*/,可能有()
double deal_2(char *low, char *high)
{
int cnt_kh=0;
char *p=high;
while( (flagch(*p)!=2 && p>low) || (cnt_kh!=0) )
{
if(*p=='(')cnt_kh++;
if(*p==')')cnt_kh--;
p--;
}
if(p<=low)
return deal_9(low, high);
else if(*p=='*')
return deal_2(low, p-1)*deal_9(p+1, high);
else if(*p=='/')
return deal_2(low, p-1)/deal_9(p+1, high);
}
// 优先级9(最高) 计算 () 或 数值转换:
// 计算 括号内的内容 或 把数值字符串转换成数值
// 把括号内的字符串视为新的计算字符串进行计算
double deal_9(char *low, char *high)
{
if( *low!='(' )
return trans(low, high);
else
return deal_1(low+1, high-1);
}
/*
int true_cnt_point(double num)
{
if( num-(int)num==0 )return 0;
return 1+true_cnt_point(num*10);
}
*/
// 按字符输入字符串, 跳过空格
void Enterstr(char *str) {
for(int i=0; i<MAX_STR; i++)
{
scanf("%c", str);
if(*str=='\n')break;
if(*str!=' ')str++;
}
*str='\0';
}
int main()
{
char str[MAX_STR+1];
while(1)
{
Enterstr(str);
printf("%.10f\n", deal_1(str, highofstr(str)));
}
return 0;
}