An alternative way to Memoize in Ruby
May 8, 2017I was screwing around with Ruby Coroutines, and the concept of coroutines in general, when I came across this page. It talks about how you can use coroutines to memoize functions. Ruby has native coroutines. You can guess what happened next.
Here’s what I came up with:
class Object
def self.fiber_memoize(method_name)
meth = self.instance_method(method_name)
self.send(:define_method, method_name) do
f = Fiber.new do |s|
result = meth.bind(self).call
loop do
Fiber.yield(result)
end
end
self.send(:define_singleton_method, method_name) do
f.resume
end
f.resume
end
end
end
This works on an instance-level, which a naive implementation wouldn’t. It also works by re-defining methods on a per-instance basis when you call them, which is just another example of how much Ruby rocks.
Here’s how to use it:
class Factorial
def initialize(num)
@num = num
end
def result
puts "This run is not memoized"
(1..@num).inject(:*) || 1
end
fiber_memoize :result
end
f = Factorial.new(100)
puts f.result #=> Prints the not memoized message, then the result
puts f.result #=> Prints the (now memoized) result
Interesting, this is also a way to do memoization in ruby without any conditionals, which is pretty neat.
Please never use this in any kind of production environment. I’m begging you.