怎么实现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*)来放回一个字符到缓冲区。

 

 #返回上一级

posted @ 2014-01-12 22:41  张海拔  阅读(716)  评论(0编辑  收藏  举报