Reverse Polish Calculator (逆波兰计算器)方案的分析——如何解决问题,从需要到实现

 

Reverse Polish Calculator (逆波兰计算器)方案的分析——如何解决问题,从需要到实现

分类: 【这就是C】   262人阅读  评论(0)  收藏  举报
逆波兰 计算器 c

Reverse Polish Calculator (逆波兰计算器)方案的分析——如何解决问题,从需要到实现

Reverse Polish Calculator (逆波兰计算器)方案的分析——如何解决问题,从需要到实现_第1张图片

注:文章素材来源于K&R第二版


需要实现的功能:

(针对用户的期望)

1、基本的四则运算

2、取模运算

*3、其它数学函数的运算。比如三角函数、幂函数、指数函数等

*4、能够调用上一步的结果(待补充)



用户可能的行为:

用户可能的输入(用代表空格,其它不可见字符使用其转义字符表示)

正常输入:

32-\n

3.52.1*\n (带有小数点)

-0.5-3/\n (带有负数)

多余空格:□□\t32-\n

小数点前不带整数:-.5-.6+\n


用户期待的结果:

能够进行多种数学运算,包括小数和负数的运算,三角函数等;

输入回车符后能立即得到正确答案,得到答案后程序不会退出,可以继续计算其他算术式,直到用户输入EOF,结束计算器的运行。



整体框架

while (下一个运算符或者操作数不是EOF )

          if (是数 )

                  将数压入栈中

          else if (是运算符 )

                 弹出所需数目的操作数

                  执行运算

                  将结果压入栈中

         else if (是换行符 )

                 弹出并打印栈顶值

          else

                 出错


    1/*

    2 *逆波兰计算器主函数

    3 */

    4

    5/*

    6 *算法描述:

    7 * while (下一个运算符或者操作数不是EOF )

    8 *  if (是数 )

    9 *      将数压入栈中

   10 *  else if (是运算符 )

   11 *      弹出所需数目的操作数

   12 *      执行运算

   13 *      将结果压入栈中

   14 *  else if (是换行符 )

   15 *      弹出并打印栈顶值

   16 *  else

   17 *      出错

   18 */

   19#include <stdio.h>

   20#include <stdlib.h>

   21#include <math.h>      //使用fmod()函数和其他数学函数

   22#include <string.h>    //使用strcmp()函数

   23

   24#define MAXOP 100

   25#define NUMBER'0'

   26#define NAME'f'

   27

   28intgetop(char s[] );

   29voidpush(double);

   30doublepop(void);

   31voidmathfunc(char s[] );

   32

   33intmain(void)

   34{

   35    int type;

   36    double op2;

   37    char s[MAXOP];

   38

   39    while( (type=getop(s)) != EOF )

   40    {

   41        switch( type)

   42        {

   43            case NUMBER:

   44                push(atof( s) );

   45                break;

   46            case'+':

   47                push(pop() +pop() );

   48                break;

   49            case'*':

   50                push(pop() *pop() );

   51                break;

   52            case'-':

   53                op2=pop();

   54                push(pop() - op2);

   55                break;

   56            case'/':

   57                op2=pop();

   58                if( op2!=0.0)

   59                    push(pop() / op2);

   60                else

   61                    printf("Error : zero divisor\n");

   62                break;

   63            /*

   64             *增加取模运算

   65             */

   66            case'%':

   67                op2=pop();

   68                if( op2!=0.0)

   69                    push(fmod(pop() , op2) );

   70                else

   71                    printf("Error : zero divisor\n");

   72                break;

   73            /*

   74             *增加其他数学函数

   75             */

   76            case NAME:

   77                mathfunc( s);

   78                break;

   79

   80            case'\n':

   81                printf("\t%.8g\n",pop() );

   82                break;

   83            default:

   84                printf("error: unknown command %s\n", s );

   85                break;

   86        }

   87    }

   88    return0;

   89}

   90

   91

   92voidmathfunc(char s[] )

   93{

   94    double op2;

   95

   96    if(strcmp( s,"sin") ==0)

   97        push(sin(pop() ) );

   98    else if(strcmp( s,"cos") ==0)

   99        push(cos(pop() ) );

  100    else if(strcmp( s,"exp") ==0)

  101        push(exp(pop() ) );

  102    else if(strcmp( s,"pow") ==0)

  103    {

  104        op2=pop();

  105        push(pow(pop(), op2) );

  106    }

  107    else

  108        printf("error: %s not supported\n", s );

  109}



如何获取操作数或者运算符(针对用户的行为)

1、获取操作符:基本的四则运算符是单个的字符,直接读入字符。

2、获取操作数:操作数是一系列数字值或者小数点符号或者负号。

3、获取输入结束符:根据用户需求,在读取一个换行符后结束输入,输出结果。

4、对错误输入的处理:读入数字字符和正常的运算符不会产生错误,对于非法的字符,直接读入,交由主函数处理。

具体获取方法:

1、由于针对操作数和运算符读取的方式不一样,操作数需要一个数组来存放一系列字符,而运算符只需要一个字符变量来存放即可,所以读取时需要进行两种类型变量的数据交换,字符变量使用返回值进行交换,字符串可以在函数中进行修改来实现数据交换。由此我们大致可以知道获取操作数和运算符的函数原型:

int getop ( char s[] );

函数返回int型值(实际为char,为了处理EOF所以为int),把一个字符数组作为函数的参数,存储读到的操作数;

2、以具体输入为例进行分析

3.52.1*\n

在这个输入中,我们希望得到两个操作数3.52.1和一个运算符*以及一个结束符\n

2.1首先处理第一个前面的(如果有的话)和关键字符之间的空白或者\t):采取的方法是跳过(连续读取,直至出现第一个不是空白的字符)空白。(空白还有一个作用就是区分负号和减号,在后面会具体谈及。)由于第一个字符可能是操作数的起始部分,而且空白的结束是以读取到下一个不是空白的字符为结束的,这个非空白的字符有可能就是操作数的一部分,所以还是应该将读取的字符存入数组。

2.2、在处理完空白之后,要及时将字符数组变为字符串,以免在其它函数使用时出错。

2.3、如果第一个非空字符不是操作数的组成部分(digit'.''-'),就把这个字符返回

2.4、如果这个字符是操作数的组成部分就“激活”字符数组,继续读取操作数的剩余部分并存入数组中,直到读到下一个不能组成操作数的字符,将字符数组转换为字符串

2.5、处理最后读取的字符,如果它是EOF直接丢弃,否则就把它重新放入输入流,供下一次读取

此时又引发一个问题,就是输入缓存,需要建立一个缓冲区,可以将“误读”的字符重新放到这个缓冲区内,供下一次读取。(稍后详谈)

2.6、正确读取到操作数后,函数需要一个返回值,由之前的假定,它不能是数组,只能是字符常量,但这个字符常量的值并无实际用途,所以只需要一个标签即可,我们把它设置为NUMBER '0'

3负号和减号的区分

当读入的字符是'-'时,如果下一个字符是数字或者小数点,那么这个'-'就是负号,否则就是减号。

4对数学函数的读取

当读入的是一个小写字母时,继续读入小写字母(如果后面有的话),并存入数组,形成字符串,将最后读入的一个不是小写字母的字符(不是EOF)放入输入中,判断字符串的长度,小于1就将读入的一个字符返回,否则就返回一个标志,表示已经读取到函数字符串,并在主程序中使用这种字符串。

 



    1/*

    2 *获取操作符或者操作数的函数

    3 */

    4#include <stdio.h>

    5#include <ctype.h>

    6#include <string.h>

    7

    8#define NUMBER'0'

    9#define NAME'f'

   10

   11intgetch(void);

   12intungetch(int c);

   13

   14intgetop(char s[] )

   15{

   16    int i, c;

   17    while( (s[0] = c =getch() ) ==' '  ||  c=='\t')

   18        ;

   19    s[1] = '\0';

   20    i=0;

   21

   22    /*

   23     *增加对其他函数字符串的读取

   24     */

   25

   26    if(islower( c) )

   27    {

   28        while(islower( s[++i] = c =getch() ) )

   29            ;

   30        s[i] = '\0';

   31        if( c!= EOF)

   32            ungetch( c);

   33        if(strlen( s) >1)

   34            return NAME;

   35        else

   36            return c;

   37    }

   38

   39    if( !isdigit( c)  &&  c!='.'  &&  c!='-')

   40        return c;

   41

   42    /*

   43     *增加对负号和减号的区分

   44     */

   45    if( c=='-')

   46        if(isdigit( c=getch() )  ||  c=='.')

   47            s[++i] = c;

   48        else

   49        {

   50            if( c!= EOF)

   51                ungetch( c);

   52            return'-';

   53        }

   54

   55    if(isdigit( c) )

   56        while(isdigit( s[++i] = c =getch() ) )

   57            ;

   58    if( c=='.')

   59        while(isdigit( s[++i] = c =getch() ) )

   60            ;

   61

   62    s[i] ='\0';

   63

   64    if( c!= EOF)

   65        ungetch( c);

   66

   67    return NUMBER;

   68}



如何实现栈(数据的组织)

我们把操作数都放到一个数组中,只要程序遇到操作符,就会将相应个数的操作数取出来,进行运算,然后将结果再次放入数组中。这种基于数组的“操作方式”(栈,实际上就是数组或者链表等,由于对其操作方式的不同,就会产生栈、列队等各种数据结构)就是栈。

它起码应该具备两个基本功能:进栈和出栈。

我们需要一个数组来存储数据,还要一个位置量来标记栈顶位置,方便进出栈。



    1/*

    2 *用于栈的操作函数

    3 */

    4#include <stdio.h>

    5#define MAXVAL 100

    6

    7int sp=0;

    8double val[MAXVAL];

    9

   10voidpush(double f)

   11{

   12    if( sp< MAXVAL)

   13        val[sp++] = f;

   14    else

   15        printf("error: stack full\n");

   16}

   17

   18doublepop(void)

   19{

   20    if( sp>0)

   21        return val[--sp];

   22    else

   23    {

   24        printf("error: empty stack\n");

   25        return0.0;

   26    }

   27}




如何实现输入缓存

我们需要一个栈(存取都发生在数组的同一端),当发生“误读”时,就将这个字符推进栈中,下一次读取时,如果栈不是空,就读取栈顶元素,否则就从标准输入读取。




    1/*

    2 *读取字符和放入被读取字符的函数

    3 */

    4#include <stdio.h>

    5#define BUFSIZE 100

    6

    7char buf[BUFSIZE];

    8int bufp=0;

    9

   10intgetch(void)

   11{

   12    return( bufp>0) ? buf[--bufp] :getchar();

   13}

   14

   15voidungetch(int c)

   16{

   17    if( bufp>= BUFSIZE)

   18        printf("ungetch: too many characters\n");

   19    else

   20        buf[ bufp++ ] = c;

   21}




  1. 最后提供源代码供大家编译运行  


  1. /* 
  2.  * 逆波兰计算器主函数 
  3.  */  
  4.   
  5. /* 
  6.  * 算法描述: 
  7.  * while ( 下一个运算符或者操作数不是EOF ) 
  8.  *  if ( 是数 ) 
  9.  *      将数压入栈中 
  10.  *  else if ( 是运算符 ) 
  11.  *      弹出所需数目的操作数 
  12.  *      执行运算 
  13.  *      将结果压入栈中 
  14.  *  else if ( 是换行符 ) 
  15.  *      弹出并打印栈顶值 
  16.  *  else 
  17.  *      出错 
  18.  */  
  19. #include <stdio.h>  
  20. #include <stdlib.h>  
  21. #include <math.h>     //使用fmod()函数和其他数学函数  
  22. #include <string.h>       //使用strcmp()函数  
  23.   
  24. #define MAXOP 100  
  25. #define NUMBER '0'  
  26. #define NAME 'f'  
  27.   
  28. int getop ( char s[] );  
  29. void push ( double );  
  30. double pop ( void );  
  31. void mathfunc ( char s[] );  
  32.   
  33. int main ( void )  
  34. {  
  35.     int type;  
  36.     double op2;  
  37.     char s[MAXOP];  
  38.   
  39.     while ( (type=getop(s)) != EOF )  
  40.     {  
  41.         switch ( type )  
  42.         {  
  43.             case NUMBER:  
  44.                 push ( atof ( s ) );  
  45.                 break;  
  46.             case '+':  
  47.                 push ( pop () + pop () );  
  48.                 break;  
  49.             case '*':  
  50.                 push ( pop () * pop () );  
  51.                 break;  
  52.             case '-':  
  53.                 op2 = pop ();  
  54.                 push ( pop () - op2 );  
  55.                 break;  
  56.             case '/':  
  57.                 op2 = pop ();  
  58.                 if ( op2 != 0.0 )  
  59.                     push ( pop () / op2 );  
  60.                 else  
  61.                     printf ( "Error : zero divisor\n" );  
  62.                 break;  
  63.             /* 
  64.              * 增加取模运算 
  65.              */  
  66.             case '%':  
  67.                 op2 = pop ();  
  68.                 if ( op2 != 0.0 )  
  69.                     push ( fmod ( pop () , op2 ) );  
  70.                 else  
  71.                     printf ( "Error : zero divisor\n" );  
  72.                 break;  
  73.             /* 
  74.              * 增加其他数学函数 
  75.              */  
  76.             case NAME:  
  77.                 mathfunc ( s );  
  78.                 break;  
  79.   
  80.             case '\n':  
  81.                 printf ( "\t%.8g\n", pop () );  
  82.                 break;  
  83.             default:  
  84.                 printf ( "error: unknown command %s\n", s );  
  85.                 break;  
  86.         }  
  87.     }  
  88.     return 0;  
  89. }  
  90.   
  91.   
  92. void mathfunc ( char s[] )  
  93. {  
  94.     double op2;  
  95.   
  96.     if ( strcmp ( s, "sin" ) == 0 )  
  97.         push ( sin ( pop () ) );  
  98.     else if ( strcmp ( s, "cos" ) == 0 )  
  99.         push ( cos ( pop () ) );  
  100.     else if ( strcmp ( s, "exp" ) == 0 )  
  101.         push ( exp ( pop () ) );  
  102.     else if ( strcmp ( s, "pow" ) == 0 )  
  103.     {  
  104.         op2 = pop ();  
  105.         push ( pow ( pop (), op2 ) );  
  106.     }  
  107.     else  
  108.         printf ( "error: %s not supported\n", s );  
  109. }  


  1. /* 
  2.  * 获取操作符或者操作数的函数 
  3.  */  
  4. #include <stdio.h>  
  5. #include <ctype.h>  
  6. #include <string.h>  
  7.   
  8. #define NUMBER '0'  
  9. #define NAME 'f'  
  10.   
  11. int getch ( void );  
  12. int ungetch ( int c );  
  13.   
  14. int getop ( char s[] )  
  15. {  
  16.     int i, c;  
  17.     while ( (s[0] = c = getch() ) == ' '  ||  c == '\t' )  
  18.         ;  
  19.     s[1] = '\0';  
  20.     i = 0;  
  21.   
  22.     /* 
  23.      * 增加对其他函数字符串的读取 
  24.      */  
  25.   
  26.     if ( islower ( c ) )  
  27.     {  
  28.         while ( islower ( s[++i] = c = getch () ) )  
  29.             ;  
  30.         s[i] = '\0';  
  31.         if ( c != EOF )  
  32.             ungetch ( c );  
  33.         if ( strlen ( s ) > 1 )  
  34.             return NAME;  
  35.         else  
  36.             return c;  
  37.     }  
  38.   
  39.     if ( !isdigit ( c )  &&  c != '.'  &&  c != '-' )  
  40.         return c;  
  41.   
  42.     /* 
  43.      * 增加对负号和减号的区分 
  44.      */  
  45.     if ( c == '-' )  
  46.         if ( isdigit ( c = getch () )  ||  c == '.' )  
  47.             s[++i] = c;  
  48.         else  
  49.         {  
  50.             if ( c != EOF )  
  51.                 ungetch ( c );  
  52.             return '-';  
  53.         }  
  54.   
  55.     if ( isdigit ( c ) )  
  56.         while ( isdigit ( s[++i] = c = getch () ) )  
  57.             ;  
  58.     if ( c == '.' )  
  59.         while ( isdigit ( s[++i] = c = getch () ) )  
  60.             ;  
  61.   
  62.     s[i] = '\0';  
  63.   
  64.     if ( c != EOF )  
  65.         ungetch ( c );  
  66.   
  67.     return NUMBER;  
  68. }  

  1. /* 
  2.  * 用于栈的操作函数 
  3.  */  
  4. #include <stdio.h>  
  5. #define MAXVAL 100  
  6.   
  7. int sp = 0;  
  8. double val[MAXVAL];  
  9.   
  10. void push ( double f )  
  11. {  
  12.     if ( sp < MAXVAL )  
  13.         val[sp++] = f;  
  14.     else  
  15.         printf ( "error: stack full\n" );  
  16. }  
  17.   
  18. double pop ( void )  
  19. {  
  20.     if ( sp > 0 )  
  21.         return val[--sp];  
  22.     else  
  23.     {  
  24.         printf ( "error: empty stack\n" );  
  25.         return 0.0;  
  26.     }  
  27. }  


  1. /* 
  2.  * 读取字符和放入被读取字符的函数 
  3.  */  
  4. #include <stdio.h>  
  5. #define BUFSIZE 100  
  6.   
  7. char buf[BUFSIZE];  
  8. int bufp = 0;  
  9.   
  10. int getch ( void )  
  11. {  
  12.     return ( bufp > 0 ) ? buf[--bufp] : getchar();  
  13. }  
  14.   
  15. void ungetch ( int c )  
  16. {  
  17.     if ( bufp >= BUFSIZE )  
  18.         printf ( "ungetch: too many characters\n" );  
  19.     else  
  20.         buf [ bufp++ ] = c;  
  21. }  

你可能感兴趣的:(【这就是C】)