Close and Go BackBack to Viget

Multi-line Memoization

David Eisinger
David Eisinger, Web Developer, January 05, 2009 6

Here’s a quick tip that came out of a code review we did last week. One easy way to add caching to your Ruby app is to memoize the results of computationally expensive methods:

def foo
  @foo ||= expensive_method
end

The first time the method is called, @foo will be nil, so expensive_method will be called and its result stored in @foo. On subsequent calls, @foo will have a value, so the call to expensive_method will be bypassed. This works well for one-liners, but what if our method requires multiple lines to determine its result?

def foo
  arg1 = expensive_method_1
  arg2 = expensive_method_2
  expensive_method_3(arg1, arg2)
end

A first attempt at memoization yields this:

def foo
  unless @foo
    arg1 = expensive_method_1
    arg2 = expensive_method_2
    @foo = expensive_method_3(arg1, arg2)
  end
  @foo
end

To me, using @foo three times obscures the intent of the method. Let’s do this instead:

def foo
  @foo ||= begin
    arg1 = expensive_method_1
    arg2 = expensive_method_2
    expensive_method_3(arg1, arg2)
  end
end

This clarifies the role of @foo and reduces LOC. Of course, if you use the Rails built-in memoize method, you can avoid accessing these instance variables entirely, but this technique has utility in situations where requiring ActiveSupport would be overkill.

Luis Lavena said on 01/05 at 10:37 PM

BTW, if you’re interested in LOC, then you should interest in Code Coverage.

Multi line memoization needs to be in one single line for RCov to mark the code as executed. Add “; \” at the end of each line (except for the end of the begin one) and RCov will report the code as executed.

Dunno why, but found that a couple of days ago.

Neeraj said on 01/06 at 12:58 AM

nice trick. Thanks.

grosser said on 01/06 at 04:09 AM

:D

thats exactly what i have been looking for, though i do not need it so badly since memoize arrived…

Lucas Sallovitz said on 01/06 at 10:13 AM

You don’t actually need to include ActiveSupport for memoization, it can be mixed in from a 20 line Module that would give you a better implementation using if instead of ||= so it handles false and nil correctly, and supporting method parameters.

Tony Pitale said on 01/07 at 11:56 AM

Just a note that you can also memoize the results of other control structures. For example, my favorite, the case statement.

Mike Harris said on 01/13 at 01:21 PM

I use the fattr gem for this.  It give you what this does, and more (it’s also an accessor, and it gives you a bang method to clear the value).

Commenting is not available in this weblog entry.

We're the Developers

at Viget Labs. We write about web development trends, tips, best practices, industry events, and our projects — all with an emphasis on Ruby on Rails.

Contact Us

Have any questions, comments, ideas, or secrets to share? Let us know.


What is the third letter in apple?

Sorry, you need to have Javascript enabled to use this form. (Don't blame us, blame the spammers!) If you'd like to contact us, please visit our Contact page.