怎么实现Linux下的逆波兰计算器dc?
@Author: 张海拔
@Update: 2014-01-12
@Link: http://www.cnblogs.com/zhanghaiba/p/3516660.html
1 /* 2 *Author: ZhangHaiba 3 *Date: 2014-1-12 4 *File: dc_linux.c 5 * 6 *a demo shows how to use a stack to implement dc which as a built-in tool in Linux 7 *but, this demo only support int number and int operation + - * / 8 */ 9 10 #include <stdio.h> 11 #include <stdlib.h> 12 #include <ctype.h> 13 #define ST_LEN 1024 //the depth of stack 14 #define BUF_SIZE 32 15 #define NUM '0' 16 17 //public from stack.h 18 void push(int); 19 int pop(); 20 int top(); 21 int is_empty(); 22 int is_full(); 23 void clear(); 24 //private form stack.c 25 int stack[ST_LEN]; 26 int sp = 0; //point to next empty space 27 28 //public form main.h 29 int token(); 30 //public form main.c 31 char buf[BUF_SIZE]; 32 int cnt = 0; 33 34 int main(void) 35 { 36 int c; 37 int op2, op1; 38 39 while ((c = token()) != EOF) { 40 switch(c) { 41 case NUM: 42 push(atoi(buf)); 43 break; 44 case '+': 45 if (size() >= 2) { 46 op2 = pop(), op1 = pop(); 47 push(op1 + op2); 48 } else 49 printf("dc: stack empty\n"); 50 break; 51 case '-': 52 if (size() >= 2) { 53 op2 = pop(), op1 = pop(); 54 push(op1 - op2); 55 } else 56 printf("dc: stack empty\n"); 57 break; 58 case '*': 59 if (size() >= 2) { 60 op2 = pop(), op1 = pop(); 61 push(op1 * op2); 62 } else 63 printf("dc: stack empty\n"); 64 break; 65 case '/': 66 if (size() >= 2) { 67 op2 = pop(), op1 = pop(); 68 push(op1 / op2); 69 } else 70 printf("dc: stack empty\n"); 71 break; 72 case 'p': 73 printf(is_empty() ? "dc: stack empty\n" : "%d\n", top()); 74 default: 75 break; 76 } 77 } 78 return 0; 79 } 80 81 int token() 82 { 83 int c = getchar(); 84 if (isdigit(c)) { 85 buf[cnt++] = c; 86 while ((c = getchar()) != EOF) { 87 if (isdigit(c)) 88 buf[cnt++] = c; 89 else { 90 buf[cnt] = '\0'; 91 cnt = 0; 92 ungetc(c, stdin); 93 return NUM; 94 } 95 } 96 } else 97 return c; 98 } 99 100 void push(int item) 101 { 102 if (!is_full()) 103 stack[sp++] = item; 104 } 105 106 int pop() 107 { 108 if (!is_empty()) 109 return stack[--sp]; 110 } 111 112 int top() 113 { 114 if (!is_empty()) 115 return stack[sp-1]; 116 } 117 118 int is_full() 119 { 120 return sp >= ST_LEN; 121 } 122 123 int is_empty() 124 { 125 return sp <= 0; 126 } 127 128 int size() 129 { 130 return sp; 131 } 132 133 void clear() 134 { 135 sp = 0; 136 }
Linux下有一个很强大计算器bc,而逆波兰计算器dc就是bc的后台。
逆波兰表达式(后缀表达式)不需要括号也不需要定义优先级就可以运算,dc就是用来处理这种“干净”的输入。
学习了栈这个数据结构,对于逆波兰表达式的计算是很容易的。
规则是:遇到操作数则压栈,遇到操作符则弹栈,先后弹出两个操作数,第一个是op2,第二是op1,然后根据操作符把计算结果再次压栈。所以栈顶值就是当前计算的结果。
这里的实现的简易dc,仅仅支持int整型,和int操作符+ - * /。
dc的用例如下(上述实现的简易版 dc,其测试用例同下):
432 13 * p
5616
3 p
3
* p
16848
* p
dc: stack empty
16848
42 / 4 p
4
p
4
上面的实现中,需要注意的是:
(1)数据封装方面,最好把stack做成一个单独模块,这样可以提供接口,隐藏stack数据本身。
(2)对于栈中元素<2时遇到操作符,不要执行单独一次的弹栈(即把两次弹栈看做一个原子操作,测试发现dc就是这样处理的)。
(3)由于输入数字不一定是一位数,所以需要一个buffer数组来存储多位数的字符表示并封装成字符串用标准库函数atoi转换,这个过程需要用ungetc(int, FILE*)来放回一个字符到缓冲区。