# Lazy lookup

**URL:** <https://www.ruby-forum.com/t/lazy-lookup/167036>\
**Category:** Ruby\
**Created:** [May 2, 2009, 3:40pm UTC](https://www.ruby-forum.com/t/lazy-lookup/167036 "2009-05-02T15:40:41Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![samoht](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/ac8455/32.png) [@samoht](https://www.ruby-forum.com/u/samoht)\
**Post date:** [May 2, 2009, 3:40pm UTC](https://www.ruby-forum.com/t/lazy-lookup/167036/1 "2009-05-02T15:40:41Z")

</div>

Hello,

how about the following implementation for lazy lookups?

def define\_lookup  
h = lambda{ |k|  
h = {}  
# … fill h (expensive!) …  
h[k]  
}  
self.class.class\_eval{ define\_method(:lookup){ |k| h[k] } }  
end

Once `define_lookup'' has been called, the method`lookup’’ is  
defined and will act in a lazy manner. Just call `lookup'' as often as you need, but only for the first time the expensive operation (e.g. dealing with files) will be performed. Due to duck typing no condition needs to be checked: for the first call`h[k]’’ implies calling the  
lambda, for the next times ``h[k]’’ implies accessing the hash.

(Tried with Ruby 1.8.6.)

Regards  
Thomas

---

<div class="post-metadata">

**Author:** ![samoht](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/ac8455/32.png) [@samoht](https://www.ruby-forum.com/u/samoht)\
**Post date:** [May 2, 2009, 4:25pm UTC](https://www.ruby-forum.com/t/lazy-lookup/167036/2 "2009-05-02T16:25:44Z")

</div>

On 02.05.2009 15:33, Thomas H. wrote:

> ```
> self.class.class_eval{ define_method(:lookup){ |k| h[k] } }
> 
> ```
> 
> end
> 
> Once `define_lookup'' has been called, the method`lookup’’ is  
> defined and will act in a lazy manner. Just call `lookup'' as often as you need, but only for the first time the expensive operation (e.g. dealing with files) will be performed. Due to duck typing no condition needs to be checked: for the first call`h[k]’’ implies calling the  
> lambda, for the next times ``h[k]’’ implies accessing the hash.
> 
> (Tried with Ruby 1.8.6.)

Sorry to sound discouraging but there is memoize already. The  
difference is that not the whole Hash is filled but only the one that  
you need for the argument list provided which seems potentially more  
efficient.

define\_lookup should probably be a class method but in that case having  
a single Hash for all instances is problematic.

Note, that you can use Hash’s default\_proc to achieve something similar  
very elegantly:

class Foo  
def initialize(some, args)  
@some = some  
@args = args  
@lookup = Hash.new {|h,k| h[k] = do\_lookup(k)}  
end

def lookup(a,b)  
@lookup[[a,b]]  
end

private  
def do\_lookup(args)  
puts “calculating…”  
sleep 2  
args.first + args.last + @some + @args  
end  
end

Note that you do not need a separate method but if the calculation is  
complex code might be more readable with another method.

irb(main):059:0\> f = Foo.new 1,2  
=\> #\<Foo:0x101b3ce8 @some=1, @args=2, @lookup={}\>  
irb(main):060:0\> f.lookup 3,4  
calculating…  
=\> 10  
irb(main):061:0\> f.lookup 3,4  
=\> 10  
irb(main):062:0\>

In case you want to fill the Hash completely you can just replace the  
calculation code to assign multiple values.

OTOH, often a single Hash with a default\_proc is sufficient already,  
e.h.

irb(main):064:0\> foo = Hash.new {|h,k| h[k] = k + 1 + 3}  
=\> {}  
irb(main):065:0\> foo[3]  
=\> 7  
irb(main):066:0\> foo  
=\> {3=\>7}

Kind regards

robert

---

<div class="post-metadata">

**Author:** ![samoht](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/ac8455/32.png) [@samoht](https://www.ruby-forum.com/u/samoht)\
**Post date:** [May 4, 2009, 4:42pm UTC](https://www.ruby-forum.com/t/lazy-lookup/167036/3 "2009-05-04T16:42:29Z")

</div>

Robert K. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote/schrieb  
[removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid):

> Sorry to sound discouraging but there is memoize already. The  
> difference is that not the whole Hash is filled but only the one  
> that you need for the argument list provided which seems potentially  
> more efficient.

In my situation this ist not the case. The key value pairs can be  
retrieved by reading a file. It’s better to open/read/close the file  
only once and retrieve all key value pairs at the same time. Of course  
the file should not be read at all, if never a value will be  
requested.

> In case you want to fill the Hash completely you can just replace the  
> calculation code to assign multiple values.

How does that look like? Somethink like that?:

Hash.new{ |h,k| h.replace(  
COMPLETELY\_CALCULATED\_HASH  
); h[k] }

(COMPLETELY\_CALCULATED\_HASH should be replaced by some code which  
calculates the hash completely)

Regards  
Thomas

---

<div class="post-metadata">

**Author:** ![samoht](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/ac8455/32.png) [@samoht](https://www.ruby-forum.com/u/samoht)\
**Post date:** [May 4, 2009, 5:05pm UTC](https://www.ruby-forum.com/t/lazy-lookup/167036/4 "2009-05-04T17:05:27Z")

</div>

2009/5/4 Robert K. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid):

> > How does that look like? Somethink like that?:
> > 
> > Hash.new{ |h,k| h.replace(  
> > COMPLETELY\_CALCULATED\_HASH  
> > ); h[k] }

PS: careful with h[k] at the end: you might create an infinite loop  
unless you are sure that k is in the Hash.

Cheers

robert

---

<div class="post-metadata">

**Author:** ![samoht](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/ac8455/32.png) [@samoht](https://www.ruby-forum.com/u/samoht)\
**Post date:** [May 4, 2009, 5:04pm UTC](https://www.ruby-forum.com/t/lazy-lookup/167036/5 "2009-05-04T17:04:15Z")

</div>

2009/5/4 Thomas H. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid):

> the file should not be read at all, if never a value will be  
> requested.
> 
> > In case you want to fill the Hash completely you can just replace the  
> > calculation code to assign multiple values.
> 
> How does that look like? Somethink like that?:
> 
> Hash.new{ |h,k| h.replace(  
> COMPLETELY\_CALCULATED\_HASH  
> ); h[k] }

> (COMPLETELY\_CALCULATED\_HASH should be replaced by some code which  
> calculates the hash completely)

I don’t see the need for #replace. You can as well hand over h to the  
routine that fetches the data. But otherwise yes. As a safety  
measure you would probably also remember that you have loaded the Hash  
in an instance variable of h.

Kind regards

robert
