K&Rを読もう(42) 演習4-3 - 4-10 変数機能つきRPN電卓
今回の演習は逆ポーランド電卓。多機能になるような演習問題で結構楽しかったです。
さて、早速遊んでみます!!
逆ポーランド電卓なのでちょっと変態的な構文です。順にいきましょう。
まずは四則演算
1 1 + 2 1 2 3 4 5 + + + + 15 3.1415 10 * 10 * 314.15
小数もいけます(基本的に小数だったり)
- 1と1を足すと2
- 1と2と3と4と5を足して足して足して足して15
- 3.1415と半径10を掛けて、半径10を掛けると314.15
日本語と同じ構文ですね。
次に数学関数。
3.1415 10 2 p * 314.15
なんと2乗できます。sがsin,cがcos,tがtan,eがexp,そしてpがpowです。これ以上のサンプルは求めないでください(笑
文字列とか面倒なので関数は全て一文字にしてみました。
勿論スタック操作も出来ます。
3.1415 y * 9.8690223 2 10 x - 8
yはyank。値を次のスタックにコピーします。xは入れ替え。うっほメンドクセェというユーザーにオススメ。
そして、変数機能。
3.1415 w P 8
w(write)で変数Pへの書き込みです。スタックから値が消えたので、前の演算で使用した8が見えてます。変数は大文字のアルファベットです。
取り出します。
r P 3.1415
r(read)でスタックへ戻して、
10 2 p * 314.15
3.1415 * pow(10,2)という演算が出来ました。
なかなか賢いです。
(RPNだと変数いらないという噂も)
ソース
個々の回答を載せると大量になるので一個だけ(やってない問題もアリ)。ソースは長いので圧縮してみました。gnu indentとか整形ツールでも使ってください。
#include <stdio.h> #include <stdlib.h> #include <math.h> #define MAXOP 100 #define NUMBER '0' double pop(void); void push(double f); double look(void); int getop(char s[]); int main(void) { int type; double op1,op2; char s[MAXOP]; int name; double var['Z' - 'A' + 1]; 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"); break; case '%' : op2 = pop(); if (op2 != 0.0) push((int) pop() % (int) op2); else printf("error: zero divisor"); break; /* 数学系 */ case 's' : push(sin(pop())); break; case 'c' : push(cos(pop())); break; case 't' : push(tan(pop())); break; case 'e' : push(exp(pop())); break; case 'p' : push(pow(pop(), pop())); break; /* スタック操作 */ case 'y' : push(look()); case 'x' : op1 = pop(); op2 = pop(); push(op1); push(op2); break; case 'w' : name = getop(s); if (isalpha(name)) var[name - 'A'] = pop(); else printf("can't write value"); break; case 'r' : name = getop(s); if (isalpha(name)) push(var[name - 'A']); else printf("can't read value"); break; /* 表示 */ case '\n' : printf("\t%.8g\n", look()); break; default : printf(" error\n type : %d\n str : %s\n", type, s); break; } } return EXIT_SUCCESS; } #define MAXVAL 100 int sp = 0; double val[MAXVAL] = {0}; void push(double f) { if (sp < MAXVAL) val[sp++] = f; else printf("error: stack full, can't push %g\n", f); } double pop(void) { if (sp > 0) return val[--sp]; else { printf("error: stack empty, can't pop\n"); return 0.0; } } double look(void) { if (sp > 0) return val[sp - 1]; else { return 0.0; } } int getch(void); void ungetch(int c); 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; } int ch = '\0'; int getch(void) { int tmp; if(ch == '\0') return getchar(); else { tmp = ch; ch = '\0'; return tmp; } } void ungetch(int c) { ch = c; } /* for ex 4-7 void ungets(char s[]) { int len = strlen(s); while(len > 0) ungetch(s[--len]); s[0] = '\0'; } */
関数機能を付ければ立派なプログラミング言語になりそうですね。
まだまだ色々手直ししたいところだらけ。気が向いたら修正するかな。