📄 memoize.3
字号:
.Sp.Vb 1\& tie my %cache => \*(AqMLDBM\*(Aq, \*(AqDB_File\*(Aq, $filename, ...;\&\& memoize \*(Aqmyfunc\*(Aq,\& NORMALIZER => \*(Aqn\*(Aq,\& SCALAR_CACHE => [HASH => \e%cache],\& LIST_CACHE => MERGE,\& ;\&\& sub n {\& my $context = wantarray() ? \*(AqL\*(Aq : \*(AqS\*(Aq;\& # ... now compute the hash key from the arguments ...\& $hashkey = "$context:$hashkey";\& }.Ve.SpThis normalizer function will store scalar context return values inthe disk file under keys that begin with \f(CW\*(C`S:\*(C'\fR, and list contextreturn values under keys that begin with \f(CW\*(C`L:\*(C'\fR..SH "OTHER FACILITIES".IX Header "OTHER FACILITIES".ie n .Sh """unmemoize""".el .Sh "\f(CWunmemoize\fP".IX Subsection "unmemoize"There's an \f(CW\*(C`unmemoize\*(C'\fR function that you can import if you want to.Why would you want to? Here's an example: Suppose you have your cachetied to a \s-1DBM\s0 file, and you want to make sure that the cache iswritten out to disk if someone interrupts the program. If the programexits normally, this will happen anyway, but if someone typescontrol-C or something then the program will terminate immediatelywithout synchronizing the database. So what you can do instead is.PP.Vb 1\& $SIG{INT} = sub { unmemoize \*(Aqfunction\*(Aq };.Ve.PP\&\f(CW\*(C`unmemoize\*(C'\fR accepts a reference to, or the name of a previouslymemoized function, and undoes whatever it did to provide the memoizedversion in the first place, including making the name refer to theunmemoized version if appropriate. It returns a reference to theunmemoized version of the function..PPIf you ask it to unmemoize a function that was never memoized, itcroaks..ie n .Sh """flush_cache""".el .Sh "\f(CWflush_cache\fP".IX Subsection "flush_cache"\&\f(CW\*(C`flush_cache(function)\*(C'\fR will flush out the caches, discarding \fIall\fRthe cached data. The argument may be a function name or a referenceto a function. For finer control over when data is discarded orexpired, see the documentation for \f(CW\*(C`Memoize::Expire\*(C'\fR, included inthis package..PPNote that if the cache is a tied hash, \f(CW\*(C`flush_cache\*(C'\fR will attempt toinvoke the \f(CW\*(C`CLEAR\*(C'\fR method on the hash. If there is no \f(CW\*(C`CLEAR\*(C'\fRmethod, this will cause a run-time error..PPAn alternative approach to cache flushing is to use the \f(CW\*(C`HASH\*(C'\fR option(see above) to request that \f(CW\*(C`Memoize\*(C'\fR use a particular hash variableas its cache. Then you can examine or modify the hash at any time inany way you desire. You may flush the cache by using \f(CW\*(C`%hash = ()\*(C'\fR..SH "CAVEATS".IX Header "CAVEATS"Memoization is not a cure-all:.IP "\(bu" 4Do not memoize a function whose behavior depends on programstate other than its own arguments, such as global variables, the timeof day, or file input. These functions will not produce correctresults when memoized. For a particularly easy example:.Sp.Vb 3\& sub f {\& time;\& }.Ve.SpThis function takes no arguments, and as far as \f(CW\*(C`Memoize\*(C'\fR isconcerned, it always returns the same result. \f(CW\*(C`Memoize\*(C'\fR is wrong, ofcourse, and the memoized version of this function will call \f(CW\*(C`time\*(C'\fR onceto get the current time, and it will return that same timeevery time you call it after that..IP "\(bu" 4Do not memoize a function with side effects..Sp.Vb 5\& sub f {\& my ($a, $b) = @_;\& my $s = $a + $b;\& print "$a + $b = $s.\en";\& }.Ve.SpThis function accepts two arguments, adds them, and prints their sum.Its return value is the numuber of characters it printed, but youprobably didn't care about that. But \f(CW\*(C`Memoize\*(C'\fR doesn't understandthat. If you memoize this function, you will get the result youexpect the first time you ask it to print the sum of 2 and 3, butsubsequent calls will return 1 (the return value of\&\f(CW\*(C`print\*(C'\fR) without actually printing anything..IP "\(bu" 4Do not memoize a function that returns a data structure that ismodified by its caller..SpConsider these functions: \f(CW\*(C`getusers\*(C'\fR returns a list of users somehow,and then \f(CW\*(C`main\*(C'\fR throws away the first user on the list and prints therest:.Sp.Vb 7\& sub main {\& my $userlist = getusers();\& shift @$userlist;\& foreach $u (@$userlist) {\& print "User $u\en";\& }\& }\&\& sub getusers {\& my @users;\& # Do something to get a list of users;\& \e@users; # Return reference to list.\& }.Ve.SpIf you memoize \f(CW\*(C`getusers\*(C'\fR here, it will work right exactly once. Thereference to the users list will be stored in the memo table. \f(CW\*(C`main\*(C'\fRwill discard the first element from the referenced list. The nexttime you invoke \f(CW\*(C`main\*(C'\fR, \f(CW\*(C`Memoize\*(C'\fR will not call \f(CW\*(C`getusers\*(C'\fR; it willjust return the same reference to the same list it got last time. Butthis time the list has already had its head removed; \f(CW\*(C`main\*(C'\fR willerroneously remove another element from it. The list will get shorterand shorter every time you call \f(CW\*(C`main\*(C'\fR..SpSimilarly, this:.Sp.Vb 3\& $u1 = getusers(); \& $u2 = getusers(); \& pop @$u1;.Ve.Spwill modify \f(CW$u2\fR as well as \f(CW$u1\fR, because both variables are referencesto the same array. Had \f(CW\*(C`getusers\*(C'\fR not been memoized, \f(CW$u1\fR and \f(CW$u2\fRwould have referred to different arrays..IP "\(bu" 4Do not memoize a very simple function..SpRecently someone mentioned to me that the Memoize module made hisprogram run slower instead of faster. It turned out that he wasmemoizing the following function:.Sp.Vb 3\& sub square {\& $_[0] * $_[0];\& }.Ve.SpI pointed out that \f(CW\*(C`Memoize\*(C'\fR uses a hash, and that looking up anumber in the hash is necessarily going to take a lot longer than asingle multiplication. There really is no way to speed up the\&\f(CW\*(C`square\*(C'\fR function..SpMemoization is not magical..SH "PERSISTENT CACHE SUPPORT".IX Header "PERSISTENT CACHE SUPPORT"You can tie the cache tables to any sort of tied hash that you wantto, as long as it supports \f(CW\*(C`TIEHASH\*(C'\fR, \f(CW\*(C`FETCH\*(C'\fR, \f(CW\*(C`STORE\*(C'\fR, and\&\f(CW\*(C`EXISTS\*(C'\fR. For example,.PP.Vb 2\& tie my %cache => \*(AqGDBM_File\*(Aq, $filename, O_RDWR|O_CREAT, 0666;\& memoize \*(Aqfunction\*(Aq, SCALAR_CACHE => [HASH => \e%cache];.Ve.PPworks just fine. For some storage methods, you need a little glue..PP\&\f(CW\*(C`SDBM_File\*(C'\fR doesn't supply an \f(CW\*(C`EXISTS\*(C'\fR method, so included in thispackage is a glue module called \f(CW\*(C`Memoize::SDBM_File\*(C'\fR which doesprovide one. Use this instead of plain \f(CW\*(C`SDBM_File\*(C'\fR to store yourcache table on disk in an \f(CW\*(C`SDBM_File\*(C'\fR database:.PP.Vb 2\& tie my %cache => \*(AqMemoize::SDBM_File\*(Aq, $filename, O_RDWR|O_CREAT, 0666;\& memoize \*(Aqfunction\*(Aq, SCALAR_CACHE => [HASH => \e%cache];.Ve.PP\&\f(CW\*(C`NDBM_File\*(C'\fR has the same problem and the same solution. (Use\&\f(CW\*(C`Memoize::NDBM_File instead of plain NDBM_File.\*(C'\fR).PP\&\f(CW\*(C`Storable\*(C'\fR isn't a tied hash class at all. You can use it to store ahash to disk and retrieve it again, but you can't modify the hash whileit's on the disk. So if you want to store your cache table in a\&\f(CW\*(C`Storable\*(C'\fR database, use \f(CW\*(C`Memoize::Storable\*(C'\fR, which puts a hashlikefront-end onto \f(CW\*(C`Storable\*(C'\fR. The hash table is actually kept inmemory, and is loaded from your \f(CW\*(C`Storable\*(C'\fR file at the time youmemoize the function, and stored back at the time you unmemoize thefunction (or when your program exits):.PP.Vb 2\& tie my %cache => \*(AqMemoize::Storable\*(Aq, $filename;\& memoize \*(Aqfunction\*(Aq, SCALAR_CACHE => [HASH => \e%cache];\&\& tie my %cache => \*(AqMemoize::Storable\*(Aq, $filename, \*(Aqnstore\*(Aq;\& memoize \*(Aqfunction\*(Aq, SCALAR_CACHE => [HASH => \e%cache];.Ve.PPInclude the `nstore' option to have the \f(CW\*(C`Storable\*(C'\fR database writtenin `network order'. (See Storable for more details about this.).PPThe \f(CW\*(C`flush_cache()\*(C'\fR function will raise a run-time error unless thetied package provides a \f(CW\*(C`CLEAR\*(C'\fR method..SH "EXPIRATION SUPPORT".IX Header "EXPIRATION SUPPORT"See Memoize::Expire, which is a plug-in module that adds expirationfunctionality to Memoize. If you don't like the kinds of policiesthat Memoize::Expire implements, it is easy to write your own plug-inmodule to implement whatever policy you desire. Memoize comes withseveral examples. An expiration manager that implements a \s-1LRU\s0 policyis available on \s-1CPAN\s0 as Memoize::ExpireLRU..SH "BUGS".IX Header "BUGS"The test suite is much better, but always needs improvement..PPThere is some problem with the way \f(CW\*(C`goto &f\*(C'\fR works under threadedPerl, perhaps because of the lexical scoping of \f(CW@_\fR. This is a bugin Perl, and until it is resolved, memoized functions will see aslightly different \f(CW\*(C`caller()\*(C'\fR and will perform a little more slowlyon threaded perls than unthreaded perls..PPSome versions of \f(CW\*(C`DB_File\*(C'\fR won't let you store data under a key oflength 0. That means that if you have a function \f(CW\*(C`f\*(C'\fR which youmemoized and the cache is in a \f(CW\*(C`DB_File\*(C'\fR database, then the value of\&\f(CW\*(C`f()\*(C'\fR (\f(CW\*(C`f\*(C'\fR called with no arguments) will not be memoized. If thisis a big problem, you can supply a normalizer function that prepends\&\f(CW"x"\fR to every key..SH "MAILING LIST".IX Header "MAILING LIST"To join a very low-traffic mailing list for announcements about\&\f(CW\*(C`Memoize\*(C'\fR, send an empty note to \f(CW\*(C`mjd\-perl\-memoize\-request@plover.com\*(C'\fR..SH "AUTHOR".IX Header "AUTHOR"Mark-Jason Dominus (\f(CW\*(C`mjd\-perl\-memoize+@plover.com\*(C'\fR), Plover Systems co..PPSee the \f(CW\*(C`Memoize.pm\*(C'\fR Page at http://www.plover.com/~mjd/perl/Memoize/for news and upgrades. Near this page, athttp://www.plover.com/~mjd/perl/MiniMemoize/ there is an article aboutmemoization and about the internals of Memoize that appeared in ThePerl Journal, issue #13. (This article is also included in theMemoize distribution as `article.html'.).PPMy upcoming book will discuss memoization (and many other fascinatingtopics) in tremendous detail. It will be published by Morgan Kaufmannin 2002, possibly under the title \fIPerl Advanced TechniquesHandbook\fR. It will also be available on-line for free. For moreinformation, visit http://perl.plover.com/book/ ..PPTo join a mailing list for announcements about \f(CW\*(C`Memoize\*(C'\fR, send anempty message to \f(CW\*(C`mjd\-perl\-memoize\-request@plover.com\*(C'\fR. This mailinglist is for announcements only and has extremely low traffic\-\-\-abouttwo messages per year..SH "COPYRIGHT AND LICENSE".IX Header "COPYRIGHT AND LICENSE"Copyright 1998, 1999, 2000, 2001 by Mark Jason Dominus.PPThis library is free software; you may redistribute it and/or modifyit under the same terms as Perl itself..SH "THANK YOU".IX Header "THANK YOU"Many thanks to Jonathan Roy for bug reports and suggestions, toMichael Schwern for other bug reports and patches, to Mike Cariaso forhelping me to figure out the Right Thing to Do About Expiration, toJoshua Gerth, Joshua Chamas, Jonathan Roy (again), Mark D. Anderson,and Andrew Johnson for more suggestions about expiration, to BrentPowers for the Memoize::ExpireLRU module, to Ariel Scolnicov fordelightful messages about the Fibonacci function, to Dion Almaer forthought-provoking suggestions about the default normalizer, to WaltMankowski and Kurt Starsinic for much help investigating problemsunder threaded Perl, to Alex Dudkevich for reporting the bug inprototyped functions and for checking my patch, to Tony Bass for manyhelpful suggestions, to Jonathan Roy (again) for finding a use for\&\f(CW\*(C`unmemoize()\*(C'\fR, to Philippe Verdret for enlightening discussion of\&\f(CW\*(C`Hook::PrePostCall\*(C'\fR, to Nat Torkington for advice I ignored, to ChrisNandor for portability advice, to Randal Schwartz for suggesting the\&'\f(CW\*(C`flush_cache\*(C'\fR function, and to Jenda Krynicky for being a light inthe world..PPSpecial thanks to Jarkko Hietaniemi, the 5.8.0 pumpking, for includingthis module in the core and for his patient and helpful guidanceduring the integration process.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -