Java 版チャーチ数を書いてみた(改)

ということで,以前書いたJava 版チャーチ数を更新しました.

以前のバージョンだと戻り値の型がjava.lang.Integer 決め打ちだったのが
気になっていたので,それをtype variable にして外に追い出しました.

簡単な説明

ついでに,以下の4つのパターンでテストを書いてみました.

気付いたこととしては,Clojure は,型がゆるい所為か
Java ソースに型変数部分を導入しても型変数部分を変更する必要が無かった.
Clojure 版はソースも短い.

Scala は,型LOVE なら,記述力も高いし気持いい.
とあいえ,型変数の<> を [] で書く.型を後に書くとか,
Java と混ぜて書くのは訳わからんくなりそう.
Clojure だと,違うところは違い,同じところは同じなので,その辺があまり気にならない気がした.*1

調べ足りないこと,Javaクロージャを作ろうとすると final が必要になるのは何故?

参考

スーパーpre 記法が,Clojure に対応していて良かった.-> d:id:hatenadiary:20100219:1266571864
ちぇりお!

以下ソース


Main.java

class Main {
    public static void main(String[] args) {
        Church zero = Church.ZERO;
        Church one = zero.inc();
        Church two = one.inc();
        Church three = one.plus(two);

        F<Integer> to_i = new F<Integer>() {
                public Integer proc(Integer x) {
                    return Integer.valueOf(x.intValue() + 1);
                }
            };

        Integer number_zero = new Integer(0);

        System.out.println(zero.proc(to_i).proc(number_zero));
        System.out.println(one.proc(to_i).proc(number_zero));
        System.out.println(two.proc(to_i).proc(number_zero));
        System.out.println(three.proc(to_i).proc(number_zero));
    }
}

F.java

public interface F<T> {
    public T proc(T x);
}

Church.java

// proc :: (a -> a) -> a -> a
// (a -> a)         is F (interface)
// proc F :: a -> a is F
// (a)              is T (anonymous type)
abstract public class Church {
    abstract public <T> F<T> proc(F<T> f);

    public static Church ZERO = new Church() {
            public <T> F<T> proc(F<T> f) {
                return new F<T>() {
                    public T proc(T x) {
                        return x;
                    }
                };
            }
        };

    public Church inc() {
        return Church.inc(this);
    }

    public Church plus(final Church n) {
        return Church.plus(this, n);
    }

    public static Church inc(final Church n) {
        return new Church() {
            public <T> F<T> proc(final F<T> f) {
                return new F<T>() {
                    public T proc(T x) {
                        return f.proc(n.proc(f).proc(x));
                    }
                };
            }
        };
    }

    public static Church plus(final Church m, final Church n) {
        return new Church() {
            public <T> F<T> proc(final F<T> f) {
                return new F<T>() {
                    public T proc(T x) {
                        return n.proc(f).proc(m.proc(f).proc(x));
                    }
                };
            }
        };
    }
}

ChurchTest.java - JUnit 版テスト

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import org.junit.runner.JUnitCore;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;

public class ChurchTest {
    private Church zero = null;
    private Church one = null;
    private Church two = null;
    private Church three = null;
    private F<Integer> to_i = null;
    private Integer i_zero = null;

    @Before
    public void before() {
        this.zero = Church.ZERO;
        this.to_i = new F<Integer>() {
            public Integer proc(Integer x) {
                return Integer.valueOf(x.intValue() + 1);
            }
        };
        this.one = zero.inc();
        this.two = one.inc();
        this.three = one.plus(two);
        this.i_zero = new Integer(0);
    }

    @After
    public void after() {
        this.zero = null;
        this.one = null;
        this.two = null;
        this.three = null;
        this.to_i = null;
        this.i_zero = null;
    }

    @Test
    public void testToI() {
        assertThat(this.to_i.proc(i_zero), is(new Integer(1)));
    }

    @Test
    public void testZero() {
        assertThat(to_i(this.zero), is(0));
    }

    @Test
    public void testOne() {
        assertThat(to_i(this.one), is(1));
    }

    @Test
    public void testTwo() {
        assertThat(to_i(this.two), is(2));
    }

    @Test
    public void testThree() {
        assertThat(to_i(this.three), is(3));
    }

    int to_i(Church n) {
        return n.proc(this.to_i).proc(this.i_zero).intValue();
    }

    public static void main(String[] args) {
        JUnitCore.main(ChurchTest.class.getName());
    }
}

ChurchTestSuite.scala - ScalaTest 版

import org.scalatest.FunSuite
 
class ChurchTestSuite extends FunSuite {
  val zero = Church.ZERO;
  val one = zero.inc;
  val two = one.inc;
  val three = two.plus(one); // 2 + 1
  val to_i = new F[java.lang.Integer] {
    def proc(x : java.lang.Integer) : java.lang.Integer = {
      java.lang.Integer.valueOf(x.intValue + 1)
    }
  }

  test("inc is increment") {
    assert(to_i.proc(0) === 1)
  }
 
  test("zero:Church convert to zero:Num (Church.ZERO)") {
    assert(c_to_i(zero) === 0)
  }

  test("one:Church convert to one:Num (Church.inc)") {
    assert(c_to_i(one) === 1)
  }

  test("two:Church convert to two:Num (Church.inc for incremented)") {
    assert(c_to_i(two) === 2)
  }

  test("three:Church convert to three:Num (Church.plus)") {
    assert(c_to_i(three) === 3)
  }

  // helper method
  def c_to_i(n : Church) : Int = {
    n.proc(to_i).proc(0).intValue
  }
}

test.clj - test-is 版

(ns test
  (:import F Church)
  (:use clojure.contrib.test-is))

(def to_i (proxy [F] [] (proc [x] (+ x 1))))
(def zero (Church/ZERO))
(def one (. zero inc))
(def two (. one inc))
(def three (. one (plus two)))

(defn c_to_i [n]
  (.. n (proc to_i) (proc 0)))

(deftest test-to_i
  (is (= 1 (. to_i (proc 0)))))

(deftest test-zero-for-static_value
  (is (= 0 (c_to_i zero))))

(deftest test-one-for-inc
  (is (= 1 (c_to_i one))))

(deftest test-two-for-inc-with-incremented-value
  (is (= 2 (c_to_i two))))

(deftest test-three-for-plus
  (is (= 3 (c_to_i three))))

(run-tests)

*1:単にScala に慣れていない所為とも言う