On LispのmemoizeをRubyに翻訳してみる

# (defun memoize (fn)
#   (let ((cache (make-hash-table :test #'equal)))
#     #'(lambda (&rest args)
#         (multiple-value-bind (val win) (gethash args cache)
#           (if win
#               val
#               (setf (gethash args cache)
#                     (apply fn args)))))))

module Memoize
  def memoize(fn)
    cache = {}
    lambda{|*args|
      val = cache[args]
      if cache.has_key?(args)
        val
      else
        cache[args] = __send__(fn, *args)
      end
    }
  end
end

def f(x)
  sleep 0.2
  x*10
end

require 'benchmark'
include Benchmark

def real(&block)
  measure(&block).real
end

include Memoize
memf = memoize(:f)
real{ memf[100] }                # => 0.197517156600952
real{ memf[100] }                # => 1.81198120117188e-05
real{ memf[100] }                # => 1.38282775878906e-05
memf[100]                        # => 1000

Kernel.module_eval { define_method(:mf, &memf) }
real{ mf(7) }                   # => 0.199476003646851
real{ mf(7) }                   # => 1.69277191162109e-05
mf(7)                           # => 70

class Hoge
  def f(x)
    sleep 0.2
    x*100
  end

  module_eval do
    extend Memoize
    define_method(:mf, &memoize(:f))
  end
end

h = Hoge.new
real { h.mf(99) }                # => 0.199654817581177
real { h.mf(99) }                # => 1.59740447998047e-05