/ruby_memoized

Memoize method return values even with different arguments.

Primary LanguageRubyMIT LicenseMIT

RubyMemoized

RubyMemoized makes it easy to memoize methods, even if they have arguments or blocks, by making memoization as easy as declaring a method private. To experiment with that code, run bin/console for an interactive prompt.

For more information about the motivation behind the gem, please see this blog post.

Installation

Add this line to your application's Gemfile:

gem 'ruby_memoized'

And then execute:

$ bundle

Or install it yourself as:

$ gem install ruby_memoized

Usage

Suppose we have the following class:

class FibonacciCalculator
  def calculate(n)
    return n if (0..1).include?(n)
    calculate(n - 1) + calculate(n - 2)
  end
end

Right now, when we try to use this FibonacciCalculator for any larger number, it takes quite a while to compute since it has to perform the method calls in line 34.

One of the ways we can make this computation much faster is to save (memoize) the pieces so they return immediately. Let's do that in a separate method:

class FibonacciCalculator
  def calculate(n)
    return n if (0..1).include?(n)
    calculate(n - 1) + calculate(n - 2)
  end

  memoized

  def memoized_calculate(n)
    return n if (0..1).include?(n)
    memoized_calculate(n - 1) + memoized_calculate(n - 2)
  end
end

Here, we put a call to the memoized class method. This class method will allow us to make sure that the outputs of any method calls below it will be saved (memoized).

Let's see the how the performance of one stacks up against the other for an n of 35.

                    user       system    total     real
with memoization    0.000000   0.000000  0.000000  ( 0.000469)
without memoization 46.010000  0.160000  46.170000 (46.597599)

Cool, but now suppose we wrote another method that we didn't want memoized after memoized_calculate. We could unmemoize it by doing the following:

class FibonacciCalculator
  include RubyMemoized

  memoized

  def memoized_calculate(n)
    return n if (0..1).include?(n)
    memoized_calculate(n - 1) + memoized_calculate(n - 2)
  end
  
  unmemoized
  
  def calculate(n)
    return n if (0..1).include?(n)
    calculate(n - 1) + calculate(n - 2)
  end
end

Alright, that sounds all well and good. But what if we really didn't wanna call include RubyMemoized everywhere but still wanted to use memoization?

We could just add it to Object!

class Object
  include RubyMemoized
end

Now we make objects that use memoized/unmemoized willy-nilly.

class RandomThing
  memoize
  def hardcore_calculation
    # TODO: Write really hardcore calculation.
  end
end

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/kklimuk/memoized. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.

License

The gem is available as open source under the terms of the MIT License.