lazy lookup

T

Thomas Hafner

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
 
R

Robert Klemme

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.)

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
 
T

Thomas Hafner

Robert Klemme said:
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
 
R

Robert Klemme

2009/5/4 Thomas Hafner said:
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?:

=A0Hash.new{ |h,k| h.replace(
=A0 =A0 =A0COMPLETELY_CALCULATED_HASH
=A0 =A0); 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


--=20
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,776
Messages
2,569,603
Members
45,192
Latest member
KalaReid2

Latest Threads

Top