#include
#define MAX_LENG 100
double calc(char str[]);
void rpn(char str[], double rpn[]);
void split(char str[], double s[]);
int isDigit(char c);
int isOperator(char c);
double atof(char s[]);
double peek();
void push(double n);
double pop();
int compare(char c1, char c2);
int type(char c);
int main() {
double f = calc("3 * (8 + 3)");
printf("%.8g\n", f);
return f;
}
制作一个简单的有四则运算功能的函数 calc()。
形参为普通表达式,如 3+4*9
得到逆波兰表达式形式的数组 {3,4,9,*,+}
while (得到数组的每个元素) {
if (是数字) {
将操作数存入栈中,如 {3,4,9}
} else if (是运算符) {
弹出两个操作数,执行相应运算,结果存入栈中;
如 {3, 36}、{39}
} else {
报错,不支持的符号
}
}
输出栈顶元素
double calc(char str[]) {
double c, temp;
double rpn_expre[MAX_LENG];
rpn(str, rpn_expre);
for (int i = 0; rpn_expre[i] != '\0'; i++) {
c = rpn_expre[i];
if (c == '+') {
push(pop() + pop());
} else if (c == '-') {
temp = pop();
push(pop() - temp);
} else if (c == '*') {
push(pop() * pop());
} else if (c == '/') {
temp = pop();
if (temp == 0.0) {
printf("error: zero divisor\n");
} else {
push(pop() / temp);
}
} else if (c == '%') {
temp = pop();
if (temp == 0.0) {
printf("error: zero divisor\n");
} else {
push((int)pop() % (int)temp);
}
} else {
push(c);
}
}
return pop();
}
将普通表达式转为逆波兰表达式 rpn()。
当 + 是运算栈顶时,由于 * 比 + 的优先级更高,直接入栈。
3+4*9
运算符和操作数单独存储,{3, 4, 9}、{+, *}
随后取出运算栈顶元素,添加到操作栈顶:{3, 4, 9, *, +}
3*4+9
...
读取到 4,并存入:{3, 4},{*}
读取到 +,由于 + 不比栈顶元素优先级高,运算栈元素全部出栈,并存入操作数栈,{3, 4, *}。
+ 再存入运算栈:{+}
读取到 9,存入:{3, 4, *, 9}
读取完毕,运算栈元素全部出栈,并存入操作数栈,{3, 4, *, 9, +}
3-(4+9)
...
读取到 4,并存入:{3, 4},{-, (}
读取到 +,由于栈顶是 (,被括号包裹的优先运算,+ 入栈:{3, 4},{-, (, +}
读取到 9,{3, 4, 9},{-, (, +}
读取到 ),将栈中 ( 之上的元素全部出栈,存入操作数栈:{3, 4, 9, +},( 也出栈:{-}
读取完毕,运算栈元素全部出栈,存入操作数栈:{3, 4, 9, +, -}
将表达式 "3+4*9" 拆分成 {3, '+', 4, '*', 9} 形式
while (得到数组中的每个元素) {
if (是数字) {
放入 double[] 中
} else if (是 ')') {
'(' 之上的元素出栈,存入 double[] 中,'(' 也出栈;
} else if (栈顶是 '(') {
当前运算符直接入栈;
} else if (当前运算符比栈顶元素优先级更高) {
当前运算符直接入栈;
} else {
'(' 之上的元素出栈,若栈中没有 '(',则所有元素出栈;
存入 double[] 中,当前运算符再存入;
}
}
运算栈元素全部出栈,存入 double[];
void rpn(char str[], double rpn[]) {
int i, j;
double s[MAX_LENG];
split(str, s);
for (i = j = 0; s[i] != '\0'; i++) {
if (isDigit(s[i])) {
rpn[j++] = s[i];
} else if (s[i] == ')') {
while (peek() != '(') {
rpn[j++] = pop();
}
pop();
} else if (s[i] == '(') {
push(s[i]);
} else if (compare(s[i], peek()) > 0) {
push(s[i]);
} else {
while (peek() != 0.0 && peek() != '(') {
rpn[j++] = pop();
}
push(s[i]);
// 考虑使用 peek() 加 pop() 查看元素,实在没必要
// while ((rpn[j++] = pop()) != 0.0) {};j--;
}
}
while (peek() != 0.0) {
rpn[j++] = pop();
}
rpn[j] = '\0';
}
将普通表达式拆分的方法 split()。
while (遍历得到表达式的每个字符) {
跳过前面的空格和制表符;
if (字符是运算符) {
存入 double[] 中;
跳过循环;
}
if (字符是数字) {
存入当前字符到临时数组 char temp[];
while (下标++, 如果字符是数字,则存入 temp);
}
到这步,字符已不是数字;
if (字符是 '.') {
while (下标++, 如果字符是数字,则存入 temp);
}
将 temp 保存的字符串,转为小数,并存入 double[] 中;
}
void split(char str[], double s[]) {
int i, j, k, c;
char temp[MAX_LENG];
for (i = k = 0; str[i] != '\0'; i++) {
temp[0] = c = str[i];
j = 0;
if (c == ' ' || c == '\t') {
continue;
}
if (isOperator(c)) {
s[k++] = c;
s[k] = '\0';
continue;
}
if (isDigit(c)) {
while (isDigit(temp[++j] = c = str[++i])) {}
}
if (c == '.') {
while (isDigit(temp[++j] = c = str[++i])) {}
}
temp[j] = '\0';
s[k++] = atof(temp);
s[k] = '\0';
i--;
}
}
int isDigit(char c) {
return c >= '0' && c <= '9';
}
int isOperator(char c) {
switch (c) {
case '+' :
case '-' :
case '*' :
case '/' :
case '(' :
case ')' :
case '%' :
return 1;
}
return 0;
}
将字符串转为小数,可参考这篇文章。
double atof(char s[]) {
int sign = 1;
int i = 0;
switch (s[0]) {
case '-':
sign = -1;
i = 1;
break;
case '+':
i = 1;
break;
}
double n = 0.0;
while (s[i] >= '0' && s[i] <= '9') {
n = 10 * n + (s[i] - '0');
i++;
}
if (s[i] == '.') {
i++;
}
double power = 1.0;
while (s[i] >= '0' && s[i] <= '9') {
n = 10 * n + (s[i] - '0');
i++;
power *= 10;
}
return sign * n/power;
}
关于栈的方法。
查看栈顶元素 peek()。
int size = 0;
double val[MAX_LENG];
double peek() {
if (size > 0) {
return val[size-1];
} else {
printf("error: stack empty\n");
return 0.0;
}
}
添加元素 push。
void push(double n) {
if (size > MAX_LENG) {
printf("errof: stack full, can't push %g\n", n);
} else {
val[size++] = n;
}
}
弹出元素 pop。
double pop() {
if (size > 0) {
return val[--size];
} else {
printf("error: stack empty\n");
return 0.0;
}
}
compare 比较不同运算符的优先级:
int compare(char c1, char c2) {
return type(c1) - type(c2);
}
int type(char c) {
switch (c) {
case '+':
case '-':
return 0;
case '*':
case '/':
case '%':
return 1;
default:
printf("error: unknown command\n");
return -1;
}
}
下面是完整的程序:
#include
#define MAX_LENG 100
double calc(char str[]);
void rpn(char str[], double rpn[]);
void split(char str[], double s[]);
int isDigit(char c);
int isOperator(char c);
double atof(char s[]);
double peek();
void push(double n);
double pop();
int compare(char c1, char c2);
int type(char c);
int main() {
double f = calc("11 * 3 + (3 + 3 - 1)");
printf("%.8g\n", f);
return f;
}
double calc(char str[]) {
double c, temp;
double rpn_expre[MAX_LENG];
rpn(str, rpn_expre);
for (int i = 0; rpn_expre[i] != '\0'; i++) {
c = rpn_expre[i];
if (c == '+') {
push(pop() + pop());
} else if (c == '-') {
temp = pop();
push(pop() - temp);
} else if (c == '*') {
push(pop() * pop());
} else if (c == '/') {
temp = pop();
if (temp == 0.0) {
printf("error: zero divisor\n");
} else {
push(pop() / temp);
}
} else if (c == '%') {
temp = pop();
if (temp == 0.0) {
printf("error: zero divisor\n");
} else {
push((int)pop() % (int)temp);
}
} else {
push(c);
}
}
return pop();
}
void rpn(char str[], double rpn[]) {
int i, j;
double s[MAX_LENG];
split(str, s);
for (i = j = 0; s[i] != '\0'; i++) {
if (s[i] == ')') {
while ((rpn[j] = pop()) != '(') {j++;};
} else if (s[i] == '(') {
push(s[i]);
} else if (compare(s[i], peek()) > 0) {
push(s[i]);
} else if (isOperator(s[i])) {
while (peek() != 0.0 && peek() != '(') {
rpn[j++] = pop();
}
push(s[i]);
} else {
rpn[j++] = s[i];
}
}
while ((rpn[j] = pop()) != 0.0) {j++;};
rpn[j] = '\0';
}
void split(char str[], double s[]) {
int i, j, k, c;
char temp[MAX_LENG];
for (i = k = 0; str[i] != '\0'; i++) {
temp[0] = c = str[i];
j = 0;
if (c == ' ' || c == '\t') {
continue;
}
if (isOperator(c)) {
s[k++] = c;
s[k] = '\0';
continue;
}
if (isDigit(c)) {
while (isDigit(temp[++j] = c = str[++i])) {}
}
if (c == '.') {
while (isDigit(temp[++j] = c = str[++i])) {}
}
temp[j] = '\0';
s[k++] = atof(temp);
s[k] = '\0';
i--;
}
}
int isDigit(char c) {
return c >= '0' && c <= '9';
}
int isOperator(char c) {
switch (c) {
case '+' :
case '-' :
case '*' :
case '/' :
case '(' :
case ')' :
case '%' :
return 1;
}
return 0;
}
double atof(char s[]) {
int sign = 1;
int i = 0;
switch (s[0]) {
case '-':
sign = -1;
i = 1;
break;
case '+':
i = 1;
break;
}
double n = 0.0;
while (s[i] >= '0' && s[i] <= '9') {
n = 10 * n + (s[i] - '0');
i++;
}
if (s[i] == '.') {
i++;
}
double power = 1.0;
while (s[i] >= '0' && s[i] <= '9') {
n = 10 * n + (s[i] - '0');
i++;
power *= 10;
}
return sign * n/power;
}
int size = 0;
double val[MAX_LENG];
double peek() {
if (size > 0) {
return val[size-1];
}
return 0.0;
}
void push(double n) {
if (size > MAX_LENG) {
printf("errof: stack full, can't push %g\n", n);
} else {
val[size++] = n;
}
}
double pop() {
if (size > 0) {
return val[--size];
} else {
return 0.0;
}
}
int compare(char c1, char c2) {
return type(c1) - type(c2);
}
int type(char c) {
switch (c) {
case '+':
case '-':
return 0;
case '*':
case '/':
case '%':
return 1;
default:
return -1;
}
}
这程序有很大的问题,如用 double[] 存储逆波兰表达式,区分不了运算符和数字,如 '+‘43、’-‘45、’*‘42、’/‘47、’%'37,同时也导致 rpn 中遍历元素,判断类型时很尴尬,应用 char[] 存储逆波兰表达式的,类似这样:“11 3 * 3 3 + 1 - +”,而不是 {11.0, 3.0, ‘*’ 3.0, 3.0, ‘+’, ‘1’, ‘-’, ‘+’},但那就不是 split 了,在 rpn 方法里还是要按空格分隔元素,有兴趣的伙伴们,可以改写下,我明天试试。
本来想定义 char[][] 类型,每个元素都是 char[] 类型,变成双引号,就好区分类型,发现不行,又或者定义引用类型,存储类型信息,可是暂时还没有学到如何返回自定义类型。
这是书中代码,必须输入逆波兰表达式(运算符在数字的后面),按下回车,输出结果。
#include
#include
#define MAXOP 100
#define NUMBER '0'
int getop(char[]);
void push(double);
double pop(void);
int main() {
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 '\n':
printf("\t%.8g\n", pop());
break;
default:
printf("error: unknown command %s\n", s);
break;
}
}
return 0;
}
#define MAXVAL 100
int sp = 0;
double val[MAXVAL];
void push(double f) {
if (sp < MAXVAL) {
val[sp++] = f;
} else {
printf("errof: stack full, can't push %g\n", f);
}
}
double pop(void) {
if (sp > 0) {
return val[--sp];
} else {
printf("error: stack empty\n");
return 0.0;
}
}
#include
int getch(void);
void ungetch(int);
int getop(char s[]) {
int i, c;
while ((s[0] = c = getch()) == ' ' || c == '\t') {}
s[1] = '\0';
if (!isdigit(c) && c != '.') {
return c;
}
i = 0;
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;
}
#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;
}
}
示例输入,不同字符使用空格隔开。
3 5 *
15