リストでstackを再現しその中に値を保存していく方法で固めました
本当にスタックが必要でしょうか?
1 + 2 * 3 = を計算するとき、1 + 2 を見た時点ではこの計算を実行できません。
次の演算子が優先順位の高い * である可能性があるからです。1 も + もどこかに
保存しておかなければならないので、スタックが必要だと考えたのですね。
演算子の * を見たら + と優先順位を比較し、2 * 3 は計算できます。
次に = が来ようが、+ が来ようが、* が来ようが 2 * 3 は計算できます。
保存しておかなければならないのは、前の値と演算子 1個だけでいいので、
スタックは必要ありません。
スタックが必要なのは、1 + 2 * ( 3 - 4 / 5 ) のように括弧がある場合です。
1 + も 2 * も 3 - も保存しておかなければなりません。
括弧が多重になるともっとたくさん保存しなければならず、しかも後から
保存した情報から先に使用しますから、スタックということです。
スタックは数値用と演算子用の 2つ用意してもいいし、
数値と演算子を一組にしたものを押し込んでいく一つのスタックでも構いません。
演算子の優先順位の比較方法が思いつかなくて自分の持ってる知識では難しいと思ったので質問しました。
優先順位の比較がそれほど難しいことでしょうか?
- と - の優先順位を 1、* と / の優先順位を 2 とすると、
単にそれを比較するだけです。
演算子の優先順位を比較せずに + より先に * を演算するやり方があります。
次のコードが理解できますか?
C
1#include <stdio.h> // getchar, scanf, printf, puts
2#include <ctype.h> // isspace
3
4int c;
5
6double factor(void) // 因数(factor)は数値
7{
8 double val = 0;
9 if (scanf("%lf", &val) == 1)
10 while (isspace(c = getchar())) ; // 次の演算子を読み込んでおく
11 else
12 c = EOF; // エラーのため入力打ち切り。次の演算子はない
13 return val;
14}
15
16double term(void) // 項(term)は因数(factor)の積商
17{
18 double val = factor();
19 while (1)
20 if (c == '*')
21 val *= factor();
22 else if (c == '/')
23 val /= factor();
24 else
25 break;
26 return val;
27}
28
29double expr(void) // 式(expression)は項(term)の和差
30{
31 double val = term();
32 while (1)
33 if (c == '+')
34 val += term();
35 else if (c == '-')
36 val -= term();
37 else
38 break;
39 return val;
40}
41
42int main(void)
43{
44 double val = expr();
45 if (c == '=')
46 printf("%.15g\n", val);
47 else
48 puts("error");
49}
もちろん、これは一つのやり方であり、演算子を比較するやり方もあります。
非効率な方法でもよいので思いつくなら、それを質問に追記してもらえれば
アドバイスできると思います。
追記
演算子の優先順位を比較するやり方も書いてみました。
C
1#include <stdio.h> // scanf, printf, puts
2
3int priority(char op)
4{
5 if (op == '+' || op == '-') return 1;
6 if (op == '*' || op == '/') return 2;
7 return 0; // op == '='
8}
9
10int main(void)
11{
12 double val[3];
13 char op[3];
14 if (scanf("%lf %c", &val[0], &op[0]) != 2) return puts("error");
15 for (int i = 1; op[i-1] != '='; i++) {
16 if (scanf("%lf %c", &val[i], &op[i]) != 2) return puts("error");
17 while (i > 0 && priority(op[i-1]) >= priority(op[i])) {
18 switch (op[--i]) {
19 case '+': val[i] += val[i+1]; break;
20 case '-': val[i] -= val[i+1]; break;
21 case '*': val[i] *= val[i+1]; break;
22 case '/': val[i] /= val[i+1]; break;
23 default: return puts("error");
24 }
25 op[i] = op[i+1];
26 }
27 }
28 printf("%.15g\n", val[0]);
29}
理解できますか?
追記2
演算子の優先順位を比較しないほうが行数が多いのですが、
工夫すれば減らすことができます。
C
1#include <stdio.h> // scanf, printf
2
3char c, op[] = "+-*/";
4
5double expr(int i)
6{
7 double val = 0;
8 if (i < 4)
9 for (val = expr(i+2); c == op[i] || c == op[i+1]; )
10 c == '+' ? val += expr(i+2) : c == '-' ? val -= expr(i+2) :
11 c == '*' ? val *= expr(i+2) : (val /= expr(i+2));
12 else if (scanf("%lf %c", &val, &c) != 2) c = 0;
13 return val;
14}
15
16int main(void)
17{
18 double val = expr(0);
19 printf(c == '=' ? "%.15g\n" : "error\n", val);
20}
expr と term がよく似ているから一つにまとめられないか、
いっそのこと factor もまとめてしまおうということで、
expr、term、factor を expr 一つにして、expr(0)、expr(2)、expr(4) の
呼出しで区別するようにしました。