四則演算をするスタックマシン(更新版)
先日書いたスタックマシンちゃんを少し修正しました。
- (前回の日記)四則演算をするスタックマシン http://d.hatena.ne.jp/torazuka/20120829/stackmachine
ちょっとした改良をするだけのつもりでしたが、恥ずかしいミスを見つけたので、メモを残すことにしました。
直した点は、次のとおりです。
import java.util.ArrayDeque; import java.util.Deque; interface Token { Deque<Integer> execute(Deque<Integer> stack); } class Operand implements Token { Integer value; Operand(Integer val) { value = val; } @Override public Deque<Integer> execute(Deque<Integer> stack) { stack.push(value); System.out.println("[スタック] <= " + value); return stack; } } interface Operator extends Token { // Deque<Integer> execute(Deque<Integer> stack); } class Add implements Operator { @Override public Deque<Integer> execute(Deque<Integer> stack) { if (stack != null) { Integer num1 = stack.pop(); System.out.println("[スタック] => " + num1); Integer num0 = stack.pop(); System.out.println("[スタック] => " + num0); stack.push(num0 + num1); System.out.println("[スタック] <= " + (num0 + num1)); } return stack; } } class Sub implements Operator { @Override public Deque<Integer> execute(Deque<Integer> stack) { if (stack != null) { Integer num1 = stack.pop(); System.out.println("[スタック] => " + num1); Integer num0 = stack.pop(); System.out.println("[スタック] => " + num0); stack.push(num0 - num1); System.out.println("[スタック] <= " + (num0 - num1)); } return stack; } } class Mul implements Operator { @Override public Deque<Integer> execute(Deque<Integer> stack) { if (stack != null) { Integer num1 = stack.pop(); System.out.println("[スタック] => " + num1); Integer num0 = stack.pop(); System.out.println("[スタック] => " + num0); stack.push(num0 * num1); System.out.println("[スタック] <= " + (num0 * num1)); } return stack; } } class Div implements Operator { @Override public Deque<Integer> execute(Deque<Integer> stack) { if (stack != null) { Integer num1 = stack.pop(); System.out.println("[スタック] => " + num1); Integer num0 = stack.pop(); System.out.println("[スタック] => " + num0); if (num1 != 0) { stack.push(num0 / num1); System.out.println("[スタック] <= " + (num0 / num1)); } } return stack; } } public class StackMachine { protected Token createToken(String s) { Token result = null; if (s.equals("+")) { result = new Add(); } else if (s.equals("-")) { result = new Sub(); } else if (s.equals("*")) { result = new Mul(); } else if (s.equals("/")) { result = new Div(); } else { result = new Operand(Integer.valueOf(s)); } return result; } protected int execute(String input) { Deque<Integer> stack = new ArrayDeque<>(); String[] splited = input.split("\\s"); if (splited != null && 0 < splited.length) { for (String s : splited) { Token token = createToken(s); token.execute(stack); } } return stack.pop(); } public static void main(String[] args) { StackMachine stackMachine = new StackMachine(); // (1 + 2) * 4 + (4 - 2) == 14 System.out.println(stackMachine.execute("1 2 + 4 * 4 2 - +")); // (1 + 4) * (3 + 7) / 5 == 10 System.out.println(stackMachine.execute("1 4 + 3 7 + * 5 /")); // 10 + 2 == 12 System.out.println(stackMachine.execute("10 2 +")); // 10 / 2 == 5 System.out.println(stackMachine.execute("10 2 /")); } }
以下、詳細です。
1. オペレータがスタックをやり取りするようにした
Operatorはスタックから値を取り出して、結果をスタックに積むという意味からも
http://d.hatena.ne.jp/torazuka/20120829/stackmachine#c1346796099
interface Operator {
Dequeexecute(Deque stack);
}
の方がいいのではないでしょうか。
こうしておけば、符号を変えるとか絶対値を求めるなどの2項演算子でない演算に対してもすぐに対応できるようになりますよ。
たしかに、そうですね! ありがとうございます。
これによって、スタックへの値のプッシュとスタックからの値のポップが、「トークンを処理する側」から「処理されるトークンの側」に移りました。
3. 2桁以上の数値に対応した
これも、鍋谷さんのコードを読んでいて、ですよねーと思ったので修正。
スペース区切りの記法を受け取るようにして、2桁以上の数値も計算できるようにしました。
4. テストデータの逆ポーランド記法が間違っていたので修正した
修正中に除算の結果がおかしくなったので、何かと思ったら、テストデータの逆ポーランド記法を間違えていました。
// 元の式 (1 + 4) * (3 + 7) / 5 == 10
こんなふうにしていました。
// 誤った記法 1 4 + 3 7 + 5 * /
ダメジャン……。こうですよね。
// 正しい記法 1 4 + 3 7 + * 5 /
前回、誤った記法をよしとしていたのは、除算の処理でオペランドの順序を逆にするという二重のミスを犯していたためです。偶然答えが合っていたわけですね。おそろしい。
白状すると、上の式は、こちらのページからお借りしました。
(1 + 4) * (3 + 7) / 5 ;通常
逆ポーランド記法への変換1
1 4 + 3 7 + 5 * / ;逆ポーランド記法
同じ間違いです。しかし、元のサイトでは、その次のページの解説で、正しい変換を紹介しています。
手元 (1 + 4) * (3 + 7) / 5
脇 ;スタート(中略)
手元 1 4 + 3 7 + * 5 /
逆ポーランド記法への変換2
脇 ;残った演算子をつけて終了
テストを書かないとこうなるし、頭を使ってテストデータを作らないとこうなる、という例ですね。テストを書きましょう。>自分