Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

Memoizing Methods

by bduggan (Pilgrim)
on Mar 29, 2006 at 16:44 UTC ( [id://540002]=perlquestion: print w/replies, xml ) Need Help??

bduggan has asked for the wisdom of the Perl Monks concerning the following question:

Hello all,

I am using flyweight objects (i.e. the objects are blessed scalars which are keys of a lexical hash), which have resource-intensive methods that don't take any arguments.

I sometimes have a way to get pre-computed values for many of these methods.

I want to do two things :
  1. Be able to tell the objects what the return values for these methods should be, so that the methods don't have to be called sometimes.
  2. Memoize any calls to these methods (on an instance level), so that the methods don't do the resource-intensive operations multiple times.
The way I've done this is as follows (_get, _set, and _is_set are methods that get, set or check the attribute of the object by looking in the lexical hash) :
# In the various classes : __PACKAGE__->setupMemoizedSubs( methodname => sub { my $self = shift; #... do resource-intensive stuff .. }, anothermethod => sub { ... }, ) # In a common base class : sub setupMemoizedSubs { my ($class,%args) = @_; no strict 'refs'; while (my ($name,$sub) = each %args) { my $private_element = "__memoized_$name"; *{"${class}::$name"} = sub { my $self = shift; $self->_set($private_element => $_[0]) if @_==1; return $self->_get($private_element) if $self->_is_set($private_element); $self->_set($private_element => $sub->($self)); $self->_get($private_element); }; } use strict 'refs'; } # Somewhere else $object->methodname("pre-computed value"); # set the value $object->methodname(); # Gets the value we set # Or if we omit the first one... $object->methodname() # Actually calls the method
So, my questions are :
  • Does it matter whether I do this with an anonymous sub in a closure (as above) or is a dummy named subroutine better? -
  • Could I have done the above using Memoize.pm somehow?
  • Any other suggested methods for accomplishing this?
thanks

Replies are listed 'Best First'.
Re: Memoizing Methods
by hv (Prior) on Mar 29, 2006 at 19:48 UTC

    Using Memoize is certainly possible, but you'll need to jump through a couple of hoops - you must only memoize() the 'get' functionality, and need to be able to clear the cached value as part of 'set' - so it's probably easiest doing it the way you already are.

    I've not used this myself, but I believe it should work something like this (untested):

    use Memoize; my(%_getscalar, %_getlist); memoize(__PACKAGE__ . '::_get', SCALAR_CACHE => [ HASH => \%_getscalar ], LIST_CACHE => [ HASH => \%_getlist ], ); # in _set() sub _set { my($self, $element, $value) = @_; ... # default normaliser my $cachekey = join "\034", $self, $element; delete $_getscalar{$cachekey}; delete $_getlist{$cachekey}; ... }

    Hugo

Re: Memoizing Methods
by davidrw (Prior) on Mar 29, 2006 at 17:07 UTC
    What about just using Cache::FileCache or similar?
    use Cache::FileCache; sub methodname { my $self = shift; my $value = shift; # optional my $cacheKey = "methodname - $self'; # or use $self->id or similar +if there is one -- just something unique for this method & instance my $cache = new Cache::FileCache( { namespace=>"YourStuff" } ); if( defined $value ){ $cache->set( $cacheKey, $value, "10 minutes" ); # this could be N +EVER_EXPIRE, too } my $result = $cache->get($key); return $result if defined $result; # do actual 'methodname' calculations here. # optionally cache the result: # $cache->set( $cacheKey, $value, "10 minutes" ); }
      I'd like to factor out the caching part, since that'll be common to all the methods. But, thanks -- Cache::MemoryCache does seem to be a handy module to use within setupMemoizedSubs().
Re: Memoizing Methods
by eric256 (Parson) on Mar 29, 2006 at 17:34 UTC

    I'm realy confused. It appears that you want to be able to call a method with a value to save that value and then without a value to get the value. THen only if you don't call it with a value you want to call the actual method and store that value, and then calling it without a value gets that value. This seems realy confusing, at least the first part where you can send it the value you want it to save. Mostly you seem just to have attributes that sometimes you want to autoload from a method, so why not break them out a little? Getter and setter as methodname and then a seperate methods load_methodname or something like that? I think anyone looking at your code is going to be pretty confused unless already familary with your cacheing mechanism.


    ___________
    Eric Hodges
      Interesting idea. Since load_methodname and setter have such similiar effects, it made sense to me to combine them. But I can see how distinguishing read-only parameterless cached methods from read-write attributes (via load_* for the former) would clarify things.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://540002]
Approved by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others exploiting the Monastery: (4)
As of 2024-04-25 15:40 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found