トップ «前の日記(2006-01-26) 最新 次の日記(2006-02-01)» 編集

Route 477



2006-01-30

[prog] 遅延評価の実装 (from Scheme:たらいまわしべんち)

遅延評価を行うと計算が速くなる「たらいまわし関数」というものを、いろいろな言語で実装してみたという話。

Rubyによる最終版はこんな感じ。

def tarai( x, y, z = nil )
  if x <= y
  then y
  else
    z ||= yield
    tarai(tarai(x-1, y, z), tarai(y-1, z, x)){ tarai(z-1, x, y) }
  end
end

puts tarai(ARGV[0].to_i, ARGV[1].to_i, ARGV[2].to_i)

taraiの中の再起呼び出しにおいて、第一、第二引数は先に計算してしまっている(遅延評価でない)。 第三引数だけ、値を計算するのではなく「値を計算するためのブロック」を渡すことによって、 実際に計算されるタイミングを遅らせている。こうすると、x <= yが成り立つ場合はzの値が計算されないので、 全てを先に計算する場合よりも速くなる。

Haskellで「遅延評価」という言葉を知ってから、何か得体の知れない黒魔術のようなものだと思ってたのだが、 意外と簡単に実現できるものだと知って驚いた。

Schemeではdelayとforceという機能で遅延評価を行う

delayは、渡された関数から「プロミス」と呼ばれるオブジェクト(引数を持たない関数として 実装できる)を作る。forceは単純にプロミスを実行するだけである。 「値を計算して渡すかわりに、計算するための関数を渡す」という考えは上のRubyのコードに似ているが、 プロミスはそれに加えて一度実行するとその結果の値をキャッシュし、二回目の実行からはそれを返すという機能も持つ。

[ruby] 型チェックの弱さを補うもの (from RubyについてJavaプログラマが知るべき10の事柄)

どっかで見たような気がするが、もう一回読んでみた。

証言: […]2年程前に、私はあることに気が付きました。それは、私がどんどん安全のために存在する型付けの機構を当てにしなくなっていたことです。単体テストが型エラーの発生を防いでいてくれていたためです。[…] そしてPythonでアプリケーションをいくつか書き、続いてRubyでも書いてみました。型に関する問題が全く発生しないことがわかったときも、全然驚きませんでしたよ。

Rubyの場合、関数に間違ったクラスのオブジェクトを渡してもその瞬間にはエラーにならず、 存在しないメソッドを呼ぼうとした時点でエラーが起こる。 だからミスを犯した関数と実際にエラーになる関数が別だったり、いろいろなミス (例えば配列の範囲外へのアクセスや、変数名のタイプミス)が 全部「undefined method xxx for nil:NilClass」というエラーになったりして、 デバッグに手間取ることがある。

他の人はどうしてるんだろうな、と思っていたが、テストを書けばこの手のエラーは避けられるのか。