Reverse Polish Calculator (逆波兰计算器)方案的分析——如何解决问题,从需要到实现
来源:互联网 发布:卖淘宝店铺有什么风险 编辑:程序博客网 时间:2024/06/02 01:50
Reverse Polish Calculator (逆波兰计算器)方案的分析——如何解决问题,从需要到实现
Reverse Polish Calculator (逆波兰计算器)方案的分析——如何解决问题,从需要到实现
注:文章素材来源于K&R第二版
需要实现的功能:
(针对用户的期望)
1、基本的四则运算
2、取模运算
*3、其它数学函数的运算。比如三角函数、幂函数、指数函数等
*4、能够调用上一步的结果(待补充)
用户可能的行为:
用户可能的输入(用□代表空格,其它不可见字符使用其转义字符表示)
正常输入:
3□2□-\n
3.5□2.1□*\n (带有小数点)
-0.5□-3□/\n (带有负数)
多余空格:□□\t3□2-□\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.5□2.1□*\n
在这个输入中,我们希望得到两个操作数3.5、2.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}
- 最后提供源代码供大家编译运行
- /*
- * 逆波兰计算器主函数
- */
- /*
- * 算法描述:
- * while ( 下一个运算符或者操作数不是EOF )
- * if ( 是数 )
- * 将数压入栈中
- * else if ( 是运算符 )
- * 弹出所需数目的操作数
- * 执行运算
- * 将结果压入栈中
- * else if ( 是换行符 )
- * 弹出并打印栈顶值
- * else
- * 出错
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <math.h> //使用fmod()函数和其他数学函数
- #include <string.h> //使用strcmp()函数
- #define MAXOP 100
- #define NUMBER '0'
- #define NAME 'f'
- int getop ( char s[] );
- void push ( double );
- double pop ( void );
- void mathfunc ( char s[] );
- int main ( void )
- {
- int type;
- double op2;
- char s[MAXOP];
- while ( (type=getop(s)) != EOF )
- {
- switch ( type )
- {
- case NUMBER:
- push ( atof ( s ) );
- break;
- case '+':
- push ( pop () + pop () );
- break;
- case '*':
- push ( pop () * pop () );
- break;
- case '-':
- op2 = pop ();
- push ( pop () - op2 );
- break;
- case '/':
- op2 = pop ();
- if ( op2 != 0.0 )
- push ( pop () / op2 );
- else
- printf ( "Error : zero divisor\n" );
- break;
- /*
- * 增加取模运算
- */
- case '%':
- op2 = pop ();
- if ( op2 != 0.0 )
- push ( fmod ( pop () , op2 ) );
- else
- printf ( "Error : zero divisor\n" );
- break;
- /*
- * 增加其他数学函数
- */
- case NAME:
- mathfunc ( s );
- break;
- case '\n':
- printf ( "\t%.8g\n", pop () );
- break;
- default:
- printf ( "error: unknown command %s\n", s );
- break;
- }
- }
- return 0;
- }
- void mathfunc ( char s[] )
- {
- double op2;
- if ( strcmp ( s, "sin" ) == 0 )
- push ( sin ( pop () ) );
- else if ( strcmp ( s, "cos" ) == 0 )
- push ( cos ( pop () ) );
- else if ( strcmp ( s, "exp" ) == 0 )
- push ( exp ( pop () ) );
- else if ( strcmp ( s, "pow" ) == 0 )
- {
- op2 = pop ();
- push ( pow ( pop (), op2 ) );
- }
- else
- printf ( "error: %s not supported\n", s );
- }
- /*
- * 获取操作符或者操作数的函数
- */
- #include <stdio.h>
- #include <ctype.h>
- #include <string.h>
- #define NUMBER '0'
- #define NAME 'f'
- int getch ( void );
- int ungetch ( int c );
- int getop ( char s[] )
- {
- int i, c;
- while ( (s[0] = c = getch() ) == ' ' || c == '\t' )
- ;
- s[1] = '\0';
- i = 0;
- /*
- * 增加对其他函数字符串的读取
- */
- if ( islower ( c ) )
- {
- while ( islower ( s[++i] = c = getch () ) )
- ;
- s[i] = '\0';
- if ( c != EOF )
- ungetch ( c );
- if ( strlen ( s ) > 1 )
- return NAME;
- else
- return c;
- }
- if ( !isdigit ( c ) && c != '.' && c != '-' )
- return c;
- /*
- * 增加对负号和减号的区分
- */
- if ( c == '-' )
- if ( isdigit ( c = getch () ) || c == '.' )
- s[++i] = c;
- else
- {
- if ( c != EOF )
- ungetch ( c );
- return '-';
- }
- if ( isdigit ( c ) )
- while ( isdigit ( s[++i] = c = getch () ) )
- ;
- if ( c == '.' )
- while ( isdigit ( s[++i] = c = getch () ) )
- ;
- s[i] = '\0';
- if ( c != EOF )
- ungetch ( c );
- return NUMBER;
- }
- /*
- * 用于栈的操作函数
- */
- #include <stdio.h>
- #define MAXVAL 100
- int sp = 0;
- double val[MAXVAL];
- void push ( double f )
- {
- if ( sp < MAXVAL )
- val[sp++] = f;
- else
- printf ( "error: stack full\n" );
- }
- double pop ( void )
- {
- if ( sp > 0 )
- return val[--sp];
- else
- {
- printf ( "error: empty stack\n" );
- return 0.0;
- }
- }
- /*
- * 读取字符和放入被读取字符的函数
- */
- #include <stdio.h>
- #define BUFSIZE 100
- char buf[BUFSIZE];
- int bufp = 0;
- int getch ( void )
- {
- return ( bufp > 0 ) ? buf[--bufp] : getchar();
- }
- void ungetch ( int c )
- {
- if ( bufp >= BUFSIZE )
- printf ( "ungetch: too many characters\n" );
- else
- buf [ bufp++ ] = c;
- }
- Reverse Polish Calculator (逆波兰计算器)方案的分析——如何解决问题,从需要到实现
- Reverse Polish Calculator (逆波兰计算器)方案的分析——如何解决问题,从需要到实现
- Java实现逆波兰表达式(Evaluate Reverse Polish Notation)
- 逆波兰表达式(Reverse Polish Notation)
- 堆栈的应用(2) 中缀算术表达式到后缀(逆波兰记法reverse polish notation)的转换及其计算 C++实现
- 堆栈的应用(2) 中缀算术表达式到后缀(逆波兰记法reverse polish notation)的转换及其计算 C++实现
- LeetCode—Evaluate Reverse Polish Notation 逆波兰写法的解析
- 【LeetCode-面试算法经典-Java实现】【150-Evaluate Reverse Polish Notation(计算逆波兰式)】
- 【LeetCode-面试算法经典-Java实现】【151-Evaluate Reverse Polish Notation(计算逆波兰式)】
- LeetCode | Evaluate Reverse Polish Notation(逆波兰式求值)
- LeetCode:150. Evaluate Reverse Polish Notation(逆波兰表达式)
- LeetCode OJ 之 Evaluate Reverse Polish Notation (求逆波兰表达式的值)
- leetcode笔记:Evaluate Reverse Polish Notation(逆波兰式的计算)
- 逆波兰计算器的实现
- Leetcode刷题记——150. Evaluate Reverse Polish Notation(计算逆波兰表达式)
- 逆波兰表示法Reverse Polish Notation
- Evaluate Reverse Polish Notation 逆波兰表达式
- Reverse Polish Notation.逆波兰表达式,笔记
- OCM_Session7_6_配置oracle用户ssh对等性
- Ubuntu源安装
- JSP(1):servlet 三种基本实现方式
- JAVA梳理(2)——JDK和JRE的关系(更新)
- cocos2Dx:把xcode的项目移植到vs2010记录
- Reverse Polish Calculator (逆波兰计算器)方案的分析——如何解决问题,从需要到实现
- MySQL入门:配置文件说明
- java基础_02_继承
- 字节流与字符流区别
- Widening Primitive Conversion & Narrowing Primitive Conversion
- Minitab16破解(简单)
- 爬虫 Heritrix 学习笔记 —— Heritrix安装与简单配置
- 内存回收与hbase
- JSP九大隐式对象