LIST_CACHE doesn't work with ties to most DBM implementations, because Memoize tries to save a listref, and DB_File etc. can only store strings. This should at least be documented. Maybe Memoize could detect the problem at TIE time and throw a fatal error.
20010623 This was added sometime prior to 20001025.
Try out MLDBM here and document it if it works.
Perhaps
memoize
should return a reference to the original function as well as one to the memoized version? But the programmer could always construct such a reference themselves, so perhaps it's not necessary. We save such a reference anyway, so a new package method could return it on demand even if it wasn't provided bymemoize
. We could even bless the new function reference so that it could have accessor methods for getting to the original function, the options, the memo table, etc.Naah.
Maybe an option for automatic expiration of cache values? (`After one day,' `After five uses,' etc.) Also possibly an option to limit the number of active entries with automatic LRU expiration.
You have a long note to Mike Cariaso that outlines a good approach that you sent on 9 April 1999.
What's the timeout stuff going to look like?
EXPIRE_TIME => time_in_sec EXPIRE_USES => num_uses MAXENTRIES => n
perhaps? Is EXPIRE_USES actually useful?
19990916: Memoize::Expire does EXPIRE_TIME and EXPIRE_USES. MAXENTRIES can come later as a separate module.
Put in a better example than
fibo
. Show an example of a nonrecursive function that simply takes a long time to run.getpwuid
for example? But this exposes the bug that you can't saymemoize('getpwuid')
, so perhaps it's not a very good example.Well, I did add the ColorToRGB example, but it's still not so good. These examples need a lot of work.
factorial
might be a better example thanfibo
.Include an example that caches DNS lookups.
Make tie for DBI (Memoize::DBI)
Maybe the default for LIST_CACHE should be MERGE anyway.
Maybe if the original function has a prototype, the module can use that to select the most appropriate default normalizer. For example, if the prototype was
($)
, there's no reason to use `join'. If it's(\@)
then it can usejoin $;,@$_[0];
instead ofjoin $;,@_;
.Ariel Scolnikov suggests using the change counting problem as an example. (How many ways to make change of a dollar?)
Jonathan Roy found a use for `unmemoize'. If you're using the Storable glue, and your program gets SIGINT, you find that the cache data is not in the cache, because Perl normally writes it all out at once from a DESTROY method, and signals skip DESTROY processing. So you could add
$sig{INT} = sub { unmemoize ... };
This means it would be useful to have a method to return references to all the currently-memoized functions so that you could say
$sig{INT} = sub { for $f (Memoize->all_memoized) { unmemoize $f; } }
19990917 There should be a call you can make to get back the cache itself. If there were, then you could delete stuff from it to manually expire data items.
19990925 Randal says that the docs for Memoize;:Expire should make it clear that the expired entries are never flushed all at once. He asked if you would need to do that manually. I said:
Right, if that's what you want. If you have EXISTS return false, it'll throw away the old cached item and replace it in the cache with a new item. But if you want the cache to actually get smaller, you have to do that yourself. I was planning to build an Expire module that implemented an LRU queue and kept the cache at a constant fixed size, but I didn't get to it yet. It's not clear to me that the automatic exptynig-out behavior is very useful anyway. The whole point of a cache is to trade space for time, so why bother going through the cache to throw away old items before you need to?
Randal then pointed out that it could discard expired items at DESTRoY or TIEHASH time, which seemed like a good idea, because if the cache is on disk you might like to keep it as small as possible.
19991219 Philip Gwyn suggests this technique: You have a load_file function that memoizes the file contexts. But then if the file changes you get the old contents. So add a normalizer that does
return join $;, (stat($_[0])[9]), $_[0];
Now when the modification date changes, the true key returned by the normalizer is different, so you get a cache miss and it loads the new contents. Disadvantage: The old contents are still in the cache. I think it makes more sense to have a special expiration manager for this. Make one up and bundle it.
19991220 But how do you make this work when the function might have several arguments, of which some are filenames and some aren't?
19991219 There should be an inheritable TIEHASH method that does the argument processing properly.
19991220 Philip Gwyn contributed a patch for this.
20001231 You should really put this in. Jonathan Roy uncovered a problem that it will be needed to solve. Here's the problem: He has:
memoize "get_items", LIST_CACHE => ["TIE", "Memoize::Expire", LIFETIME => 86400, TIE => ["DB_File", "debug.db", O_CREAT|O_RDWR, 0666] ];
This won't work, because memoize is trying to store listrefs in a DB_File. He owuld have gotten a fatal error if he had done this:
memoize "get_items", LIST_CACHE => ["TIE", "DB_File", "debug.db", O_CREAT|O_RDWR, 0666]'
But in this case, he tied the cache to Memoize::Expire, which is *not* scalar-only, and the check for scalar-only ties is missing from Memoize::Expire. The inheritable method can take care of this.
20010623 I decided not to put it in. Instead, we avoid the problem by getting rid of TIE. The HASH option does the same thing, and HASH is so simple to support that a module is superfluous.
20001130 Custom cache manager that checks to make sure the function return values actually match the memoized values.
20001231 Expiration manager that watches cache performance and accumulates statistics. Variation: Have it automatically unmemoize the function if performance is bad.
20220828 Nah. This is way overcomplicated.
20010517 Option to have normalizer modify @_ for use by memoized function. This would save code and time in cases like the one in the manual under 'NORMALIZER', where both f() and normalize_f() do the same analysis and make the same adjustments to the hash. If the normalizer could make the adjustments and save the changes in @_, you wouldn't have to do it twice.
20010623 Add CLEAR methods to tied hash modules.
20010623 You get a warning if you try to use DB_File as LIST_CACHE, because it won't store lists. But if you use it as the underlying cache with an expiration manager in the middle, no warning---the expiration manager doesn't know it's managing a list cache, and memoize doesn't know that DB_File is underlying. Is this fixable? Probably not, but think about it.