吴昊品命令行解释程序 Round 2 —— 一个带括号的四则运算表达式的解释器(逆波兰式RPN)

  如图所示,此为两个基于android的计算器。简易的计算器是不带括号的,你在输入的时候要考虑到运算的优先级,而稍微复杂一些的计算器是带括号的,在 优先级上面的考虑更为细致。两者的理论皆为逆波兰式,我们先说简易版的计算器,然后再升华为附带括号的优先级考虑更细致的四则计算器。

 逆波兰式是神马

 波兰式(Reverse Polish notation,RPN,或逆波兰记法),也叫后缀表达式(将运算符写在操作数之后,当然,也存在前缀表达式和中缀表达式)

  逆波兰式的作用

  对于实现逆波兰式算法,难度并不大,但为什么要将看似简单的中序表达式转换为复杂的逆波兰式?原因就在于这个简单是相对人类的思维结构来说的,对计算机而言中序表达式是非常复杂的结构。相对的,逆波兰式在计算机看来却是比较简单易懂的结构。因为计算机普遍采用的内存结构是栈式结构,它执行先进后出的顺序。

 逆波兰式的实现

 理论永远是纠结的,附上一张图,便于理解……

 

   首先需要分配2个栈,一个作为临时存储运算符的栈S1(含一个结束符号),一个作为输入逆波兰式的栈S2(空栈),S1栈可先放入优先级最低的运算符#,注意,中缀式应以此最低优先级的运算符结束。可指定其他字符,不一定非#不可。从中缀式的左端开始取字符,逐序进行如下步骤:

(1)若取出的字符是操作数,则分析出完整的运算数,该操作数直接送入S2栈

(2)若取出的字符是运算符,则将该运算符与S1栈栈顶元素比较,如果该运算符优先级大于S1栈栈顶运算符优先级,则将该运算符进S1栈,否则,将S1栈的栈顶运算符弹出,送入S2栈中,直至S1栈栈顶运算符低于(不包括等于)该运算符优先级,则将该运算符送入S1栈。

(3)若取出的字符是“(”,则直接送入S1栈栈顶。

(4)若取出的字符是“)”,则将距离S1栈栈顶最近的“(”之间的运算符,逐个出栈,依次送入S2栈,此时抛弃“(”。

(5)重复上面的1~4步,直至处理完所有的输入字符

(6)若取出的字符是“#”,则将S1栈内所有运算符(不包括“#”),逐个出栈,依次送入S2栈。

完成以上步骤,S2栈便为逆波兰式输出结果。不过S2应做一下逆序处理。便可以按照逆波兰式的计算方法计算了!

 

 简易版(不带括号) 

 一个不带括号的,数值都为非负整数的四则运算表达式的解释器(输出保留两位数):

   Source: HDOJ 1237 

  Input:

  //这里格式很严格,整数和运算符之间必须有空格

  1 + 2

 4 + 2 * 5 - 7 / 11

 Output:

   3.00

   13.36

   Solve:

 利用cal函数进行四则运算,compare函数来判定优先级的高低,用一个字符串数组读入整个一行表达式,再将其分别用两个数组装填,一个装操作数,一个装操作符。

 

  1  #include<stdio.h>
  2  #include<string.h>
  3  
  4  //这里假设读入的表达式的字符数不会超过200左右的级别(当然,如果超过的话也不是一般的计算器了)
  5  #define Max 205
  6  
  7  //加减乘除四则运算,这里不考虑b不等于0的异常
  8  double cal(double a,double b,char c)
  9  {
 10    switch(c)
 11    {
 12      case '+' : return a+b;
 13      case '-' : return a-b;
 14      case '*' : return a*b;
 15      case '/' : return a/b;         
 16    }         
 17  }
 18  
 19  //这里用compare函数来判断优先级,如果<的话表示前者比后者的优先级低,以此类推
 20  char compare(char a,char b)
 21  {
 22    if(a=='+'||a=='-')
 23    {
 24      if(b=='+'||b=='-'return '=';
 25      if(b=='*'||b=='/'return '<';                  
 26    }     
 27    else
 28    {
 29      if(b=='+'||b=='-'return '>';
 30      if(b=='*'||b=='/'return '=';    
 31    }
 32  }
 33  
 34  int main()
 35  {
 36    //分别存贮表达式(字符串),操作符和数值
 37    char str[Max];
 38    char oper[Max];
 39    double num[Max];
 40    //变量v作为十进制的转换
 41    double v;
 42    //这里用了一个寄存器存储i,利用这种方式可以得到较快的速率,但问题是寄存器毕竟有限,要慎用
 43    register int i;
 44    int len,b,a;
 45    //每次读入完整的一行
 46    while(gets(str))
 47    {
 48      //如果遇到一行只有一个'0'的情况,则表示退出
 49      if(str[0]=='0'&&strlen(str)==1)
 50        break;
 51      len=strlen(str);
 52      //赋予初始值,a,b分别是数值和运算符的装入下标
 53      a=b=0;
 54      for(i=0;i<len;i++)
 55      {
 56        if(str[i]>='0')
 57        {
 58          v=0;
 59          //如果还没有读到结束的话,空格符的ASC码也是大于'0'的
 60          while(str[i]!=' '&&i!=len)
 61          {
 62            v=v*10+(str[i]-'0');
 63            i++;                
 64          }     
 65          //又见这种方式,num[a++],很优美
 66          num[a++]=v;
 67          continue;          
 68        }          
 69        else
 70        {
 71          //要保证已经读入了一个运算符 
 72          if(b!=0)
 73          {
 74            //在进行总运算之间就把优先级比较高的运算处理了
 75            while((compare(str[i],oper[b-1])!='>')&&(b>=1)&&(i!=len))
 76            {
 77              //处理一次运算
 78              num[a-2]=cal(num[a-2],num[a-1],oper[b-1]);
 79              //数值和运算符的个数都减少一个
 80              b--;
 81              a--;                                                         
 82            }     
 83            oper[b++]=str[i++];
 84            continue;   
 85          }
 86          else
 87          {
 88            //读入第一个运算符,每次不忘i++
 89            oper[b++]=str[i];
 90            i++;    
 91          }    
 92          continue;
 93        }        
 94      }      
 95      //剩下的低优先级的运算可以在这里就地处理          
 96      while(b&&b>=1)
 97      {
 98        num[a-2]=cal(num[a-2],num[a-1],oper[b-1]);
 99        b--;
100        a--;              
101      }
102      printf("%.2lf\n",num[0]);
103    }
104    return 0;    
105  }
106 

 复杂版(带括号)

 这里运用token数组存贮整个字符串的数组,利用n作为字符串数组的下标,利用match函数作为符号的匹配,报告异常。括号的优先级最高,其次是乘 除,最后是加减。这里对于除法,考虑到了除数不等于0的异常,甚至对于浮点数的算术也考虑到了,通过atof函数将字符串转换为浮点数。

 

 

  1 #include<stdio.h>
  2  #include<ctype.h>
  3  #include<stdlib.h>
  4  
  5  char token[61]; /*存放表达式字符串的数组*/
  6  int n=0;
  7  
  8  void error(void/*报告错误函数*/
  9  {
 10    printf("ERROR!\n");
 11    exit(1);
 12  }
 13  
 14  void match(char expected) /*检查字符匹配的函数*/
 15  {
 16    if(token[n]==expected)
 17      token[++n]=getchar();
 18    else error();
 19  }
 20  
 21  double term(void); /*计算乘除的函数*/
 22  double factor(void); /*处理括号和数字的函数*/
 23  
 24  double exp(void/*计算加减的函数*/
 25  {
 26    double temp=term();
 27    while((token[n]=='+')||(token[n]=='-'))
 28    {
 29      switch(token[n])
 30      {
 31        case'+':
 32          match('+');
 33          temp+=term();
 34          break;
 35        case'-':
 36          match('-');
 37          temp-=term();
 38          break;
 39      }
 40    }
 41    return temp;
 42  }
 43  
 44  double term(void)
 45  {
 46    double div;
 47    double temp=factor();
 48    while((token[n]=='*')||(token[n]=='/'))
 49    {
 50      switch(token[n])
 51      {
 52        case'*':
 53          match('*');
 54          temp*=factor();
 55          break;
 56        case'/':
 57          match('/');
 58          div=factor();
 59          if(div==0/*处理除数为零的情况*/
 60          {
 61            printf("The divisor is zero!\n");
 62            exit(1);
 63          }
 64          temp/=div;
 65          break;
 66      }
 67    }
 68    return temp;
 69  }
 70  
 71  double factor(void)
 72  {
 73    double temp;
 74    char number[61];
 75    int i=0;
 76    if(token[n]=='(')
 77    {
 78      match('(');
 79      temp=exp();
 80      match(')');
 81    }
 82    else if(isdigit(token[n])||token[n]=='.')
 83    {
 84      while(isdigit(token[n])||token[n]=='.'/*将字符串转换为浮点数*/
 85      {
 86        number[i++]=token[n++];
 87        token[n]=getchar();
 88      }
 89      number='\0';
 90      temp=atof(number);
 91    }
 92    else error();
 93    return temp;
 94  }
 95  
 96  int main()
 97  {
 98    double result;
 99    FILE *data=fopen("61590_4.dat","at");
100    if(data==NULL)
101      data=fopen("61590_4.dat","wt");
102    if(data==NULL)
103      return 0;
104    token[n]=getchar();
105    result=exp();
106    if(token[n]=='\n')
107    {
108      token[n]='\0';
109      printf("%s=%g\n",token,result);
110      fprintf(data,"%s=%g\n",token,result);
111    }
112    else error();
113    fclose(data);
114    return 0;
115    //这里主要起到一个暂停界面的作用
116    getch();
117  }

 

posted on 2013-02-28 13:00  吴昊系列  阅读(383)  评论(0编辑  收藏  举报

导航