index.md (1657B)
1 --- 2 title: "Multi-line Memoization" 3 date: 2009-01-05T00:00:00+00:00 4 draft: false 5 canonical_url: https://www.viget.com/articles/multi-line-memoization/ 6 --- 7 8 Here's a quick tip that came out of a code review we did last week. One 9 easy way to add caching to your Ruby app is to 10 [memoize](https://en.wikipedia.org/wiki/Memoization) the results of 11 computationally expensive methods: 12 13 ```ruby 14 def foo 15 @foo ||= expensive_method 16 end 17 ``` 18 19 The first time the method is called, `@foo` will be `nil`, so 20 `expensive_method` will be called and its result stored in `@foo`. On 21 subsequent calls, `@foo` will have a value, so the call to 22 `expensive_method` will be bypassed. This works well for one-liners, but 23 what if our method requires multiple lines to determine its result? 24 25 ```ruby 26 def foo 27 arg1 = expensive_method_1 28 arg2 = expensive_method_2 29 expensive_method_3(arg1, arg2) 30 end 31 ``` 32 33 A first attempt at memoization yields this: 34 35 ```ruby 36 def foo 37 unless @foo 38 arg1 = expensive_method_1 39 arg2 = expensive_method_2 40 @foo = expensive_method_3(arg1, arg2) 41 end 42 43 @foo 44 end 45 ``` 46 47 To me, using `@foo` three times obscures the intent of the method. Let's 48 do this instead: 49 50 ```ruby 51 def foo 52 @foo ||= begin 53 arg1 = expensive_method_1 54 arg2 = expensive_method_2 55 expensive_method_3(arg1, arg2) 56 end 57 end 58 ``` 59 60 This clarifies the role of `@foo` and reduces LOC. Of course, if you use 61 the Rails built-in [`memoize` 62 method](http://ryandaigle.com/articles/2008/7/16/what-s-new-in-edge-rails-memoization), 63 you can avoid accessing these instance variables entirely, but this 64 technique has utility in situations where requiring ActiveSupport would 65 be overkill.