📄 memoize.3
字号:
.Ve.PPEach of these options is optional; you can include some, all, or noneof them..Sh "\s-1INSTALL\s0".IX Subsection "INSTALL"If you supply a function name with \f(CW\*(C`INSTALL\*(C'\fR, memoize will installthe new, memoized version of the function under the name you give.For example,.PP.Vb 1\& memoize(\*(Aqfib\*(Aq, INSTALL => \*(Aqfastfib\*(Aq).Ve.PPinstalls the memoized version of \f(CW\*(C`fib\*(C'\fR as \f(CW\*(C`fastfib\*(C'\fR; without the\&\f(CW\*(C`INSTALL\*(C'\fR option it would have replaced the old \f(CW\*(C`fib\*(C'\fR with thememoized version..PPTo prevent \f(CW\*(C`memoize\*(C'\fR from installing the memoized version anywhere, use\&\f(CW\*(C`INSTALL => undef\*(C'\fR..Sh "\s-1NORMALIZER\s0".IX Subsection "NORMALIZER"Suppose your function looks like this:.PP.Vb 6\& # Typical call: f(\*(Aqaha!\*(Aq, A => 11, B => 12);\& sub f {\& my $a = shift;\& my %hash = @_;\& $hash{B} ||= 2; # B defaults to 2\& $hash{C} ||= 7; # C defaults to 7\&\& # Do something with $a, %hash\& }.Ve.PPNow, the following calls to your function are all completely equivalent:.PP.Vb 6\& f(OUCH);\& f(OUCH, B => 2);\& f(OUCH, C => 7);\& f(OUCH, B => 2, C => 7);\& f(OUCH, C => 7, B => 2);\& (etc.).Ve.PPHowever, unless you tell \f(CW\*(C`Memoize\*(C'\fR that these calls are equivalent,it will not know that, and it will compute the values for theseinvocations of your function separately, and store them separately..PPTo prevent this, supply a \f(CW\*(C`NORMALIZER\*(C'\fR function that turns theprogram arguments into a string in a way that equivalent argumentsturn into the same string. A \f(CW\*(C`NORMALIZER\*(C'\fR function for \f(CW\*(C`f\*(C'\fR abovemight look like this:.PP.Vb 5\& sub normalize_f {\& my $a = shift;\& my %hash = @_;\& $hash{B} ||= 2;\& $hash{C} ||= 7;\&\& join(\*(Aq,\*(Aq, $a, map ($_ => $hash{$_}) sort keys %hash);\& }.Ve.PPEach of the argument lists above comes out of the \f(CW\*(C`normalize_f\*(C'\fRfunction looking exactly the same, like this:.PP.Vb 1\& OUCH,B,2,C,7.Ve.PPYou would tell \f(CW\*(C`Memoize\*(C'\fR to use this normalizer this way:.PP.Vb 1\& memoize(\*(Aqf\*(Aq, NORMALIZER => \*(Aqnormalize_f\*(Aq);.Ve.PP\&\f(CW\*(C`memoize\*(C'\fR knows that if the normalized version of the arguments isthe same for two argument lists, then it can safely look up the valuethat it computed for one argument list and return it as the result ofcalling the function with the other argument list, even if theargument lists look different..PPThe default normalizer just concatenates the arguments with character28 in between. (In \s-1ASCII\s0, this is called \s-1FS\s0 or control\-\e.) Thisalways works correctly for functions with only one string argument,and also when the arguments never contain character 28. However, itcan confuse certain argument lists:.PP.Vb 3\& normalizer("a\e034", "b")\& normalizer("a", "\e034b")\& normalizer("a\e034\e034b").Ve.PPfor example..PPSince hash keys are strings, the default normalizer will notdistinguish between \f(CW\*(C`undef\*(C'\fR and the empty string. It also won't workwhen the function's arguments are references. For example, consider afunction \f(CW\*(C`g\*(C'\fR which gets two arguments: A number, and a reference toan array of numbers:.PP.Vb 1\& g(13, [1,2,3,4,5,6,7]);.Ve.PPThe default normalizer will turn this into something like\&\f(CW"13\e034ARRAY(0x436c1f)"\fR. That would be all right, except that asubsequent array of numbers might be stored at a different locationeven though it contains the same data. If this happens, \f(CW\*(C`Memoize\*(C'\fRwill think that the arguments are different, even though they areequivalent. In this case, a normalizer like this is appropriate:.PP.Vb 1\& sub normalize { join \*(Aq \*(Aq, $_[0], @{$_[1]} }.Ve.PPFor the example above, this produces the key \*(L"13 1 2 3 4 5 6 7\*(R"..PPAnother use for normalizers is when the function depends on data otherthan those in its arguments. Suppose you have a function whichreturns a value which depends on the current hour of the day:.PP.Vb 10\& sub on_duty {\& my ($problem_type) = @_;\& my $hour = (localtime)[2];\& open my $fh, "$DIR/$problem_type" or die...;\& my $line;\& while ($hour\-\- > 0)\& $line = <$fh>;\& } \& return $line;\& }.Ve.PPAt 10:23, this function generates the 10th line of a data file; at3:45 \s-1PM\s0 it generates the 15th line instead. By default, \f(CW\*(C`Memoize\*(C'\fRwill only see the \f(CW$problem_type\fR argument. To fix this, include thecurrent hour in the normalizer:.PP.Vb 1\& sub normalize { join \*(Aq \*(Aq, (localtime)[2], @_ }.Ve.PPThe calling context of the function (scalar or list context) ispropagated to the normalizer. This means that if the memoizedfunction will treat its arguments differently in list context than itwould in scalar context, you can have the normalizer function selectits behavior based on the results of \f(CW\*(C`wantarray\*(C'\fR. Even if called ina list context, a normalizer should still return a single string..ie n .Sh """SCALAR_CACHE""\fP, \f(CW""LIST_CACHE""".el .Sh "\f(CWSCALAR_CACHE\fP, \f(CWLIST_CACHE\fP".IX Subsection "SCALAR_CACHE, LIST_CACHE"Normally, \f(CW\*(C`Memoize\*(C'\fR caches your function's return values into anordinary Perl hash variable. However, you might like to have thevalues cached on the disk, so that they persist from one run of yourprogram to the next, or you might like to associate some otherinteresting semantics with the cached values..PPThere's a slight complication under the hood of \f(CW\*(C`Memoize\*(C'\fR: There areactually \fItwo\fR caches, one for scalar values and one for list values.When your function is called in scalar context, its return value iscached in one hash, and when your function is called in list context,its value is cached in the other hash. You can control the cachingbehavior of both contexts independently with these options..PPThe argument to \f(CW\*(C`LIST_CACHE\*(C'\fR or \f(CW\*(C`SCALAR_CACHE\*(C'\fR must either be one ofthe following four strings:.PP.Vb 4\& MEMORY\& FAULT\& MERGE\& HASH.Ve.PPor else it must be a reference to a list whose first element is one ofthese four strings, such as \f(CW\*(C`[HASH, arguments...]\*(C'\fR..ie n .IP """MEMORY""" 4.el .IP "\f(CWMEMORY\fR" 4.IX Item "MEMORY"\&\f(CW\*(C`MEMORY\*(C'\fR means that return values from the function will be cached inan ordinary Perl hash variable. The hash variable will not persistafter the program exits. This is the default..ie n .IP """HASH""" 4.el .IP "\f(CWHASH\fR" 4.IX Item "HASH"\&\f(CW\*(C`HASH\*(C'\fR allows you to specify that a particular hash that you supplywill be used as the cache. You can tie this hash beforehand to giveit any behavior you want..SpA tied hash can have any semantics at all. It is typically tied to anon-disk database, so that cached values are stored in the database andretrieved from it again when needed, and the disk file typicallypersists after your program has exited. See \f(CW\*(C`perltie\*(C'\fR for morecomplete details about \f(CW\*(C`tie\*(C'\fR..SpA typical example is:.Sp.Vb 3\& use DB_File;\& tie my %cache => \*(AqDB_File\*(Aq, $filename, O_RDWR|O_CREAT, 0666;\& memoize \*(Aqfunction\*(Aq, SCALAR_CACHE => [HASH => \e%cache];.Ve.SpThis has the effect of storing the cache in a \f(CW\*(C`DB_File\*(C'\fR databasewhose name is in \f(CW$filename\fR. The cache will persist after theprogram has exited. Next time the program runs, it will find thecache already populated from the previous run of the program. Or youcan forcibly populate the cache by constructing a batch program thatruns in the background and populates the cache file. Then when youcome to run your real program the memoized function will be fastbecause all its results have been precomputed..ie n .IP """TIE""" 4.el .IP "\f(CWTIE\fR" 4.IX Item "TIE"This option is no longer supported. It is still documented only toaid in the debugging of old programs that use it. Old programs shouldbe converted to use the \f(CW\*(C`HASH\*(C'\fR option instead..Sp.Vb 1\& memoize ... [TIE, PACKAGE, ARGS...].Ve.Spis merely a shortcut for.Sp.Vb 5\& require PACKAGE;\& { my %cache;\& tie %cache, PACKAGE, ARGS...;\& }\& memoize ... [HASH => \e%cache];.Ve.ie n .IP """FAULT""" 4.el .IP "\f(CWFAULT\fR" 4.IX Item "FAULT"\&\f(CW\*(C`FAULT\*(C'\fR means that you never expect to call the function in scalar(or list) context, and that if \f(CW\*(C`Memoize\*(C'\fR detects such a call, itshould abort the program. The error message is one of.Sp.Vb 2\& \`foo\*(Aq function called in forbidden list context at line ...\& \`foo\*(Aq function called in forbidden scalar context at line ....Ve.ie n .IP """MERGE""" 4.el .IP "\f(CWMERGE\fR" 4.IX Item "MERGE"\&\f(CW\*(C`MERGE\*(C'\fR normally means the function does not distinguish between listand sclar context, and that return values in both contexts should bestored together. \f(CW\*(C`LIST_CACHE => MERGE\*(C'\fR means that list contextreturn values should be stored in the same hash that is used forscalar context returns, and \f(CW\*(C`SCALAR_CACHE => MERGE\*(C'\fR means thesame, mutatis mutandis. It is an error to specify \f(CW\*(C`MERGE\*(C'\fR for both,but it probably does something useful..SpConsider this function:.Sp.Vb 1\& sub pi { 3; }.Ve.SpNormally, the following code will result in two calls to \f(CW\*(C`pi\*(C'\fR:.Sp.Vb 3\& $x = pi();\& ($y) = pi();\& $z = pi();.Ve.SpThe first call caches the value \f(CW3\fR in the scalar cache; the secondcaches the list \f(CW\*(C`(3)\*(C'\fR in the list cache. The third call doesn't callthe real \f(CW\*(C`pi\*(C'\fR function; it gets the value from the scalar cache..SpObviously, the second call to \f(CW\*(C`pi\*(C'\fR is a waste of time, and storingits return value is a waste of space. Specifying \f(CW\*(C`LIST_CACHE =>MERGE\*(C'\fR will make \f(CW\*(C`memoize\*(C'\fR use the same cache for scalar and listcontext return values, so that the second call uses the scalar cachethat was populated by the first call. \f(CW\*(C`pi\*(C'\fR ends up being called onlyonce, and both subsequent calls return \f(CW3\fR from the cache, regardlessof the calling context..SpAnother use for \f(CW\*(C`MERGE\*(C'\fR is when you want both kinds of return valuesstored in the same disk file; this saves you from having to deal withtwo disk files instead of one. You can use a normalizer function tokeep the two sets of return values separate. For example:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -