吴昊品命令行解释程序 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  }

 

你可能感兴趣的:(round)