Koan

Ryo Tomidokoro 氏の エントリ を読んで知った。


調べてみたところ Ruby 発祥の禅問答集のようなもので様々な言語で存在するみたいだ*1
禅の公案(Koan)がプログラミング学習でプチブーム:Rails Hub情報局:エンジニアライフ


Groovy は cjudd 氏の groovy_koans がよさそうだったのでやってみた。

  void testCollectionType() {
    assert __ == [].class
  }


このような感じで __ を埋めていくテストケースが現時点で 93 問あった。基本的な問題だがいろいろ間違えたし勉強になった。
Scheme 手習い』にも似ているが既に穴埋めで実行形式になっている分、手軽に始められる。


実行するとヨーダに覚悟を問われる。

Do or do not... there is no try. - Master Yoda


最近、『数のマジック』の問題を Groovy の PowerAssert を使って Koan のようなことをやっていた。

// 練習問題 17.2.2
mod(7) {
  assert 2*3 / 5 == 4
  assert 2/5 * 3 == 4
  assert 3/5 * 2 == 4
  assert 1/5*2*3 == 4
}

// 例題 20.4.1
mod(65) {
  assert 6**441 == 31
}

公式だけのクラスは別に作成した。本に合わせて自分で拡張して行くのが楽しいと思うので operator overloading が問題になった mod の部分だけ*2
Number を operator overloading して内部で呼び出すと無限ループになる。
operator overloading が有効なのは Groovy の範囲だけなので計算は直接 NumberMath を呼び出して回避した。

import org.codehaus.groovy.runtime.typehandling.NumberMath
...
  // --- ch13 ---
  // φ
  static def φ = { n ->
    (1..NumberMath.subtract(n,1)).findAll { gcd(it,n) == 1 }.size()
    // factorize(n).countBy { it }.inject(n) { acc, v -> (acc * (v.key - 1)).intdiv(v.key) }
  }

  // --- ch15 ---
  // mod
  static class ModOps {
    static def num = new ThreadLocal()
    static def mabs(n) { n < 0 ? mabs(n + num.get()) : n }
    static Number plus    (Number self, Number right) { mabs(NumberMath.add     (self, right) % num.get()) }
    static Number multiply(Number self, Number right) { mabs(NumberMath.multiply(self, right) % num.get()) }
    static Number minus   (Number self, Number right) { (0..<num.get()).find { right + it == self } }
    static Number div     (Number self, Number right) { (0..<num.get()).find { right * it == self } }
    static Number power   (Integer self, Integer right)    { power(self as Number, right as Number) }
    static Number power   (BigInteger self, Integer right) { power(self as Number, right as Number) }
    static Number power   (Number self, Number right) {
      def n = num.get()
      if (gcd(self, n) != 1) return
      right = right % φ(n)
      def x = 1
      def w = self
      while (right) {
        if (right & 1) x *= w
        w *= w
        right >>>= 1
      }
      x
    }
    static Number root    (Number self, Number right) {
      def n = num.get()
      def k = φ(n)
      if (gcd(n, right) == 1 && gcd(self, k) == 1)
        right**mod(k) { 1/self }
    }
  }
  static def mod = { n, block ->
    def old
    try {
      old = ModOps.num.get()
      ModOps.num.set(n)
      use(ModOps, block)
    } finally {
      ModOps.num.set(old)
    }
  }
...

最初に要求されるのは中学レベルだが最終的には公開暗号鍵まで学べるすごい本だった。
数学って面白いなと思って次に線形代数を始めたのだが...つまらなくて半分ぐらいでやめてしまった。
著者が数学が苦手なのは解けないからだという認識でいるので怪しいとは思ったのだが面白くないから解かないし苦手という人も大勢いるだろう*3。『行列のマジック』を出版して欲しい。
でも考えてみれば Koan も脈絡がなくただ解くだけでヨーダの言うとおり覚悟さえあれば違ったのかもしれない。


ちなみに行列計算は commons-math を使った。
java-matrix-benchmarkJava の行列ライブラリの一覧があるので他のを使ってもいいかもしれない。
ただ assert するには分数を使えないと誤差がでるので注意する。ほとんどのライブラリは実践向けで double しか使えないようだ。commons-math 以外では colt で Object 型の行列をサポートしていた*4


Haskell は見当たらないので次は Scala と Functional Koan をやる。

2011-05-20 追記

Scala 現時点の全 63 問完了。クラスはまだあったので今後も楽しみ。タイムスタンプをチェックしているのか保存すると自動でチェックされるのもよかった。
Functional Koan は関数型のイディオムというより関数型言語の Koan だった。HaskellScala もあるが Haskell は文字化け、Scala もいまいちだったのでメインの Clojure で。lein をインストールして実行してみたが wgetcurl を要求されたので script/run で実行した。Clojure ほとんど知らないけど問題が知識ではなく処理内容を問う感じで Scala よりさくさく解ける気がする。残り2,3ファイル。
そこから難しかったが完了。destructuring.clj は apply str で解いたけど違う気がする。Namaste。

*1:Ruby はいろいろでてくるのがすごいな

*2:どこかにライブラリか API がある気がする

*3:解き方の説明は分かり易い

*4:ように見えた