メモ化された手続きを簡単に記述できるモジュールを書いてみた

Hash でお手軽にメモ化 で書いた Hash を用いたメモ化が結構気に入ったので,メタプログラミングの練習がてら,もう少し簡単にわかりやすく書けるモジュールを作ってみた.

こんな風に使える.

  # ブロックを与えて呼び出すと,Memo のスコープ内にブロックで指定したメソッドを生成する
  Memo.fib do |n|
    if n < 2
      1
    else
      Memo.fib(n-1) + Memo.fib(n-2)
    end
  end

  # 生成した後は普通に呼び出して使える.
  # 処理はメモ化されているので,フィボナッチ数列1000 項目も一瞬で計算できる
  puts Memo.fib 1000
  # => 70330367711422815821835254877183549770181...

実装はこんな感じ. Ruby の動的で柔軟なインターフェースのおかげで,簡単に実装できる.Ruby すごい.

module Memo
  @@memo ||= {}

  def self.method_missing(name, *args, &block)
    raise ArgumentError unless block_given? && args.empty?

    @@memo[name] = Hash.new do |h, k|
      h[k] = block.call(*k)
    end

    define_singleton_method(name) do |*a|
      @@memo[name][a]
    end unless method_defined? name
  end
end

本当は Memo.define(:fib) do ... とかして,メソッドを定義していることを明示したほうが良いのかもしれないけれど,method_missing 使ってみたかったのと DSL っぽくしてみたかったので Memo.fib do ... だけで定義できるようにしてみた.その代わり,定義時(最初に呼び出した時点)でブロックを付けなければ ArgumentError を出すようにしてある.