- 締切済み
逆ポーランドで電卓
100 10 + = 110 この答えを下のようにするには、どうしたらいいですか? [100] [10] + = 110 教えて下さい。 下にプログラムを書いて置いたので、指摘をして下さい。 #include <stdio.h> #include <stdlib.h> #include <ctype.h> #define isSpace(c) isspace((c) & 0xFF) #define isDigit(c) isdigit((c) & 0xFF) #define isAlpha(c) isalpha((c) & 0xFF) char buf[1024], buf2[1024], *p, *p2, c, o[] = "+-*/"; int get(void) { do c = *p++; while (isSpace(c)); return c; } void expr(const char *s) { char c2; if (*s) for (expr(s+2); c==s[0] || c==s[1]; ) c2 = c, expr(s+2), p2 += sprintf(p2, " %c", c2); else if (get() == '.' || isDigit(c)) p2 += sprintf(p2, " %.15g", strtod(p-1, &p)), get(); else if (c == '(') expr(o), c == ')' ? get() : (c = 1); else if (c == '-') expr(s), p2 += sprintf(p2, " _"); else if (isAlpha(c)) exit(0); else c = 1; } double calc(char *p) { double v[100]; int i = 0; for(;;) switch (*p++) { case '\0': return v[i-1]; case ' ': break; case '_': v[i-1] = -v[i-1]; break; case '+': --i; v[i-1] += v[i]; break; case '-': --i; v[i-1] -= v[i]; break; case '*': --i; v[i-1] *= v[i]; break; case '/': --i; v[i-1] /= v[i]; break; default: v[i++] = strtod(p-1, &p); break; } } int main(void) { (fgets(p = buf, sizeof buf, stdin)); *(p2 = buf2) = 0,expr(o), c ? puts(" エラー") : printf("%s = %.15g\n", buf2, calc(buf2)); return 0; }
- みんなの回答 (1)
- 専門家の回答
みんなの回答
- ultraCS
- ベストアンサー率44% (3956/8947)
ソース見るのが面倒なで、別の解法です(なのでアドバイス) まず一点、逆ポーランド記法ではイコールは意味を持ちません。 100 10 + の時点で、オペレーター'+'が働き、結果110が表示されていなくてはなりません。まあ、表示するという意味で=を使っても良いですが で、どうプログラムするかですが、FORTHの実装が参考になるかと思います。簡単に言えば、仮想スタックを作成し、オペレーターは全てスタック間で行うようにします、こうすれば、複雑なものも実装可能です。 やり方としては、 先ず、入力文字列の処理ですが、スペースを全て00hに置き換えてしまいます。その後で、それ以外の文字の先頭のアドレスをポインタの配列で返します、これで、この配列はASCIZ文字列の先頭一覧になります。 面倒なので、数値はfloatにしますね char *instr, buff[1024]; char *strptr[512]; int nstr, lenstr; int istr, nstk; float data, data_stk[64]; instr = gets(buff); for(nstr=0;(*instr != NULL) && (*instr != CR); instr++){ if(instr == ' '){ *instr = NULL; lenstr = 0; } else{ if(lenstr == 0) strptr[nstr++] = instr; lenstr++; } } こんな感じかな、今作ったので、イメージだけつかんでください(エラー処理はしてません) で、先頭から順に処理していくわけですが、ここで仮想的なスタックを作成します。スタックについては、調べてみてください。 nst=0; for(istr=0; istr<nstr; istr++){ if(strptr[istr] 数値だったら){ data_stk[nstk++] = 数値; } elseif(strptr[istr] 二項演算子だったら){ if(nstk < 2) "stack empty"を表示して終了 } else{ data=data_stk[--nstk] "op" data_stk[--nstk]; data_stk[nstp++] = data; } } elseif(strptr[istr] 単項演算子だったら){ if(nstk < 1) "stack empty"を表示して終了 } else{ data= "op" data_stk[--nstk]; data_stk[nstk++] = data; } else{ エラーまたは終了 } } printf("%f\n",data_stk[nstk-1]; こんな感じで、延々と続けられます、二項演算であれば演算対象をスタックトップとその次にして、演算したらそのスタックは消費、結果を再びスタックトップにおくというのを繰り返し、演算後は常にスタックトップを表示するようにします。こうすれば、機能追加も関数電卓も思いのまま。 あと、sstk(show stack)やcstk(clear stack)などのスタック操作を追加したり、途中経過の表示などをオプションとして追加すると更に便利でしょう。 15分で書いたので、大きな間違いがないことを行っています。