SICP : Ex2.06 : Java

[id:lethevert:20060113:p1]
チャーチ数を表現する問題。
クラスという余計な装飾がついたせいで、意味的には分かりにくくなってしまったかも。動作やできることはScheme版やConcurrent Clean版と同じ。
手続きを抽象化して、抽象化した手続きに対する手続き(つまり高階関数)を書くというテクニックは、関数型言語で非常に有用なプログラミングテクニックだが、Javaでも同じことができるということだ。
ちなみに、テストコードには、加算を使ったケースと、リスト操作を使ったケースの2種類を記述しておいた。

import java.util.*;

abstract class F<X> {
  abstract X $(X x); //Haskell-like ;p
}

abstract class Church<X> {
  abstract X eval(F<X> f, X x);

  /* Some Calculations */
  Church<X> add_1() {
    final Church<X> m = this;
    return new Church<X>()
      { X eval(F<X> f, X x) { return f.$(m.eval(f,x));}};
  }
  Church<X> add(final Church<X> n) {
    final Church<X> m = this;
    return new Church<X>()
      { X eval(F<X> f, X x) { return m.eval(f, n.eval(f, x));}};
  }
  Church<X> mult(final Church<X> n) {
    final Church<X> m = this;
    return new Church<X>()
      { X eval(final F<X> f, X x)
          { F<X> mf = new F<X>() { X $(X x) { return m.eval(f, x);}};
            return n.eval(mf, x);
          }
      };
  }
}

class Zero<X> extends Church<X> {
  X eval(F<X> f, X x) { return x;}
}

public class Main {
  public static void main(String[] args) {
    //Simple Integer
    Church<Integer> zero = new Zero<Integer>();
    Church<Integer> three = zero.add_1().add_1().add_1();
    Church<Integer> five  = three.add_1().add_1();

    F<Integer> f = new F<Integer>() { Integer $(Integer x) { return x+1;}};

    System.out.println(zero.eval(f,0));
    System.out.println(three.eval(f,0));
    System.out.println(five.eval(f,0));

    System.out.println(three.add(five).eval(f,0));
    System.out.println(three.mult(five).eval(f,0));

    //List
    Church<List> zeroL = new Zero<List>();
    Church<List> threeL = zeroL.add_1().add_1().add_1();
    Church<List> fiveL = threeL.add_1().add_1();

    F<List> fL = new F<List>() { List $(List x) { x.add(1); return x;}};

    System.out.println(zeroL.eval(fL,new LinkedList()).size());
    System.out.println(threeL.eval(fL,new LinkedList()).size());
    System.out.println(fiveL.eval(fL,new LinkedList()).size());

    System.out.println(threeL.add(fiveL).eval(fL,new LinkedList()).size());
    System.out.println(threeL.mult(fiveL).eval(fL,new LinkedList()).size());
  }
}