Java で逆ポ

http://d.hatena.ne.jp/torazuka/20120829/stackmachine
で始まった逆ポ。

私も Java でも書いた方がいいような気がして、書いてみた。
今回の逆ポは、階乗を意味する「!」がある。

```java:Rpn.java
import java.util.ArrayDeque;
import java.util.Deque;

class TestData {
  final String input;
  final double expected;

  TestData(String i, double e) {
    input = i;
    expected = e;
  }

  static final TestData[] data = new TestData[]{
      new TestData("1", 1),
      new TestData("1 2 +", 3),
      new TestData("3 1 -", 2),
      new TestData("10 2 /", 5),
      new TestData("2 5 8 + +", 15),
      new TestData("2 5 8 + *", 26),
      new TestData("2 5 8 * +", 42),
      new TestData("1 2 + 3 4 + *", 21),
      new TestData("2 3 * 4 5 * +", 26),
      new TestData("3 ! !", 720),
      new TestData("4 1 - ! 1 + ! 56 /", 90),
  };
}

interface RpnItem {
  void process(Deque<Double> items);
}

abstract class RpnUnaryOp implements RpnItem {
  @Override
  public void process(Deque<Double> items) {
    double first = items.pop();
    items.push(calc(first));
  }

  abstract double calc(double f);
}

class RpnFact extends RpnUnaryOp {
  private double fact(double r) {
    if (r <= 1) {
      return 1;
    }
    return r * fact(r - 1);
  }

  @Override
  double calc(double f) {
    return fact(f);
  }
}

abstract class RpnBiOp implements RpnItem {
  @Override
  public void process(Deque<Double> items) {
    double first = items.pop();
    double second = items.pop();
    items.push(calc(first, second));
  }

  abstract double calc(double f, double s);
}

class RpnAdd extends RpnBiOp {
  double calc(double f, double s) {
    return f + s;
  }
}

class RpnSub extends RpnBiOp {
  double calc(double f, double s) {
    return -f + s;
  }
}

class RpnDiv extends RpnBiOp {
  double calc(double f, double s) {
    return s / f;
  }
}

class RpnMul extends RpnBiOp {
  double calc(double f, double s) {
    return f * s;
  }
}

class RpnNumber implements RpnItem {
  private double number;

  public RpnNumber(double v) {
    number = v;
  }

  @Override
  public void process(Deque<Double> items) {
    items.push(number);
  }
}

class RpnItemFactory {
  static RpnItem create(String token) {
    if (token.equals("+")) {
      return new RpnAdd();
    } else if (token.equals("-")) {
      return new RpnSub();
    } else if (token.equals("*")) {
      return new RpnMul();
    } else if (token.equals("/")) {
      return new RpnDiv();
    } else if (token.equals("!")) {
      return new RpnFact();
    } else {
      return new RpnNumber(Double.parseDouble(token));
    }
  }
}

public class Rpn {
  public static void main(String[] args) {
    for (TestData td : TestData.data) {
      double actual = solveRon(td.input);
      if (actual != td.expected) {
        System.out.printf("%s -> %f ( expected %f )\n", td.input, actual, td.expected);
      } else {
        System.out.printf("%s -> %f\n", td.input, actual);
      }
    }
  }

  private static double solveRon(String input) {
    Deque<Double> items = new ArrayDeque<Double>();
    for (String token : input.split("\\s")) {
      RpnItemFactory.create(token).process(items);
    }
    return items.pop();
  }
}
```

コメントをいっさい書かずに 140行。
テストデータのクラスなどを度外視しても100行を下回らないのはどうかとも思ったが、まあ普通にスタックマシンを書いたらこれぐらいじゃないかな。
トークンに分けるためのコードを書かずに済んでいるだけましとも思う。

RpnFactoryクラスが自分としては最大の不満なんだけれども、こういうもん？

Rpn と書くべきところを Ron とタイポしているのを発見。とほほ。