This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
# Memoization is an optimization technique used primarily to speed up
|
||||
# programs by having function calls avoid repeating the calculation
|
||||
# of results for previously-processed inputs.
|
||||
#
|
||||
# When you "memoize" a method/function using Memoizable its results are cached
|
||||
# so that later calls return results from the cache instead of recalculating
|
||||
# them.
|
||||
#
|
||||
# class T
|
||||
# include Memoizable
|
||||
#
|
||||
# def initialize(a)
|
||||
# @a = a
|
||||
# end
|
||||
#
|
||||
# def a
|
||||
# "#{@a ^ 3 + 4}"
|
||||
# end
|
||||
#
|
||||
# memoize :a
|
||||
# end
|
||||
#
|
||||
# t = T.new(10)
|
||||
# (t.a.__id__ == t.a.__id__) #=> true
|
||||
#
|
||||
# This method can also be used at the instance level to cache singleton
|
||||
# (qua class) methods by including it in the singleton class.
|
||||
#
|
||||
#--
|
||||
# TODO: It would be very cool if Memoizable could set-up default parameters
|
||||
# via #[] method, e.g. `include Memoizable[:freeze=>true]`.
|
||||
#++
|
||||
|
||||
module Memoizable
|
||||
|
||||
@cache = Hash.new{|h,k|h[k]=Hash.new{|h,k|h[k]={}}}
|
||||
|
||||
#
|
||||
def self.cache
|
||||
@cache
|
||||
end
|
||||
|
||||
#
|
||||
def self.append_features(base)
|
||||
Module == base ? super(base) : base.extend(self)
|
||||
end
|
||||
|
||||
# Directive for making your functions faster by trading space for time.
|
||||
# When you "memoize" a method/function using #memoize its results are
|
||||
# cached so that later calls with the same arguments return results from
|
||||
# the cache instead of recalculating them.
|
||||
#
|
||||
# The #memoize method also handles a few options to alter
|
||||
# behavior of the memoization:
|
||||
#
|
||||
# :class => true cache per-class not per-object
|
||||
# :freeze => true freeze the memoized return values
|
||||
# :arguments => false do not index cache by arguments
|
||||
#
|
||||
def memoize(*method_names)
|
||||
options = Hash === method_names.last ? method_names.pop : {}
|
||||
|
||||
options[:arguments] = true if options[:arguments].nil? # default to true
|
||||
|
||||
ref = options[:class] ? 'self.class.name' : 'self'
|
||||
frz = options[:freeze] ? '.freeze' : ''
|
||||
arg = options[:arguments] ? '[__a__, block_given?]' : 'nil'
|
||||
|
||||
code = ""
|
||||
method_names.each do |m|
|
||||
code << <<-code
|
||||
alias_method '#{ m }:memo', '#{ m }'
|
||||
private '#{ m }:memo'
|
||||
def #{ m }(*__a__,&__b__)
|
||||
c = Memoizable.cache[#{ref}][:'#{ m }']
|
||||
k = #{arg}
|
||||
if c.has_key?(k)
|
||||
c[k]
|
||||
else
|
||||
c[k] = __send__('#{ m }:memo',*__a__,&__b__)#{frz}
|
||||
end
|
||||
end
|
||||
code
|
||||
end
|
||||
module_eval(code)
|
||||
end
|
||||
|
||||
# Remove the memoized value from the memoization cache.
|
||||
# The next time a memoized methos is called if will be
|
||||
# remomoized.
|
||||
def rememoize(*method_names)
|
||||
if Memoizable.cache[self]
|
||||
if method_names.empty?
|
||||
Memoizable.cache.delete(self)
|
||||
else
|
||||
method_names.each do |m|
|
||||
Memoizable.cache[self].delete(m.to_sym)
|
||||
end
|
||||
end
|
||||
end
|
||||
if Memoizable.cache[self.class.name]
|
||||
if method_names.empty?
|
||||
Memoizable.cache.delete(self.class.name)
|
||||
else
|
||||
method_names.each do |m|
|
||||
Memoizable.cache[self.class.name].delete(m.to_sym)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
def unmemoize(*method_names)
|
||||
rememoize(*method_names)
|
||||
method_names.each do |m|
|
||||
alias_method name, "#{ m }:memo"
|
||||
end
|
||||
end
|
||||
|
||||
# Include Memoizable alone with an #initialize_copy method that
|
||||
# will copy the memoization cache when #dup or #clone is used.
|
||||
#
|
||||
# TODO: Is this best approach? Perhaps a method to easily create
|
||||
# the #initialize_copy method would suffice?
|
||||
module Copy
|
||||
|
||||
#
|
||||
def self.included(base)
|
||||
base.extend(Memoizable)
|
||||
end
|
||||
|
||||
#
|
||||
def initialize_copy(original)
|
||||
if Memoizable.cache.key?(original)
|
||||
Memoizable.cache[self] = Memoizable.cache[original].dup
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user