Updated Memoize to CPAN version 1.03
authorChris 'BinGOs' Williams <chris@bingosnet.co.uk>
Fri, 8 Jun 2012 12:39:20 +0000 (13:39 +0100)
committerChris 'BinGOs' Williams <chris@bingosnet.co.uk>
Sun, 17 Jun 2012 20:26:49 +0000 (21:26 +0100)
  [DELTA]

  1.03  Sun, 22 Apr 2012 17:11:16 -0400
    * Clarified MERGE cache options and fixed bug
    * Documentation fixes

Porting/Maintainers.pl
cpan/Memoize/Memoize.pm
cpan/Memoize/Memoize/AnyDBM_File.pm
cpan/Memoize/Memoize/Expire.pm
cpan/Memoize/Memoize/ExpireFile.pm
cpan/Memoize/Memoize/ExpireTest.pm
cpan/Memoize/Memoize/NDBM_File.pm
cpan/Memoize/Memoize/SDBM_File.pm
cpan/Memoize/Memoize/Storable.pm
cpan/Memoize/TODO
cpan/Memoize/t/array_confusion.t

index 6bf3241..af26180 100755 (executable)
@@ -1222,7 +1222,7 @@ use File::Glob qw(:case);
 
     'Memoize' => {
         'MAINTAINER'   => 'mjd',
-        'DISTRIBUTION' => 'FLORA/Memoize-1.02.tar.gz',
+        'DISTRIBUTION' => 'MJD/Memoize-1.03.tgz',
         'FILES'        => q[cpan/Memoize],
         'EXCLUDED'     => ['article.html'],
         'UPSTREAM'     => 'cpan',
index 1ebc174..9a58c4a 100644 (file)
@@ -3,13 +3,13 @@
 #
 # Transparent memoization of idempotent functions
 #
-# Copyright 1998, 1999, 2000, 2001 M-J. Dominus.
+# Copyright 1998, 1999, 2000, 2001, 2012 M. J. Dominus.
 # You may copy and distribute this program under the
 # same terms as Perl itself.  If in doubt, 
 # write to mjd-perl-memoize+@plover.com for a license.
 
 package Memoize;
-$VERSION = '1.02';
+$VERSION = '1.03';
 
 # Compile-time constants
 sub SCALAR () { 0 } 
@@ -132,10 +132,9 @@ sub memoize {
   # Perhaps I should check here that you didn't supply *both* merge
   # options.  But if you did, it does do something reasonable: They
   # both get merged to the same in-memory hash.
-  if ($options{SCALAR_CACHE} eq 'MERGE') {
+  if ($options{SCALAR_CACHE} eq 'MERGE' || $options{LIST_CACHE} eq 'MERGE') {
+    $options{MERGED} = 1;
     $caches{SCALAR} = $caches{LIST};
-  } elsif ($options{LIST_CACHE} eq 'MERGE') {
-    $caches{LIST} = $caches{SCALAR};
   }
 
   # Now deal with the TIE options
@@ -240,11 +239,12 @@ sub _memoizer {
     my $cache = $info->{S};
     _crap_out($info->{NAME}, 'scalar') unless $cache;
     if (exists $cache->{$argstr}) { 
-      return $cache->{$argstr};
+      return $info->{O}{MERGED}
+        ? $cache->{$argstr}[0] : $cache->{$argstr};
     } else {
       my $val = &{$info->{U}}(@_);
       # Scalars are considered to be lists; store appropriately
-      if ($info->{O}{SCALAR_CACHE} eq 'MERGE') {
+      if ($info->{O}{MERGED}) {
        $cache->{$argstr} = [$val];
       } else {
        $cache->{$argstr} = $val;
@@ -255,17 +255,10 @@ sub _memoizer {
     my $cache = $info->{L};
     _crap_out($info->{NAME}, 'list') unless $cache;
     if (exists $cache->{$argstr}) {
-      my $val = $cache->{$argstr};
-      # If LISTCONTEXT=>MERGE, then the function never returns lists,
-      # so we have a scalar value cached, so just return it straightaway:
-      return ($val) if $info->{O}{LIST_CACHE} eq 'MERGE';
-      # Maybe in a later version we can use a faster test.
-
-      # Otherwise, we cached an array containing the returned list:
-      return @$val;
+      return @{$cache->{$argstr}};
     } else {
       my @q = &{$info->{U}}(@_);
-      $cache->{$argstr} = $info->{O}{LIST_CACHE} eq 'MERGE' ? $q [0] : \@q;
+      $cache->{$argstr} = \@q;
       @q;
     }
   } else {
@@ -360,7 +353,7 @@ Memoize - Make functions faster by trading space for time
 
 =head1 SYNOPSIS
 
-        # This is the documentation for Memoize 1.02
+        # This is the documentation for Memoize 1.03
        use Memoize;
        memoize('slow_function');
        slow_function(arguments);    # Is faster than it was before
@@ -652,7 +645,7 @@ the following four strings:
        MERGE
         HASH
 
-or else it must be a reference to a list whose first element is one of
+or else it must be a reference to an array whose first element is one of
 these four strings, such as C<[HASH, arguments...]>.
 
 =over 4
@@ -690,21 +683,24 @@ runs in the background and populates the cache file.  Then when you
 come to run your real program the memoized function will be fast
 because all its results have been precomputed.
 
+Another reason to use C<HASH> is to provide your own hash variable.
+You can then inspect or modify the contents of the hash to gain finer
+control over the cache management.
+
 =item C<TIE>
 
 This option is no longer supported.  It is still documented only to
 aid in the debugging of old programs that use it.  Old programs should
 be converted to use the C<HASH> option instead.
 
-        memoize ... [TIE, PACKAGE, ARGS...]
+        memoize ... ['TIE', PACKAGE, ARGS...]
 
 is merely a shortcut for
 
         require PACKAGE;
-       { my %cache;
-          tie %cache, PACKAGE, ARGS...;
-       }
-        memoize ... [HASH => \%cache];
+       { tie my %cache, PACKAGE, ARGS...;
+          memoize ... [HASH => \%cache];
+        }
 
 =item C<FAULT>
 
@@ -717,35 +713,66 @@ should abort the program.  The error message is one of
 
 =item C<MERGE>
 
-C<MERGE> normally means the function does not distinguish between list
-and sclar context, and that return values in both contexts should be
-stored together.  C<LIST_CACHE =E<gt> MERGE> means that list context
-return values should be stored in the same hash that is used for
-scalar context returns, and C<SCALAR_CACHE =E<gt> MERGE> means the
-same, mutatis mutandis.  It is an error to specify C<MERGE> for both,
-but it probably does something useful.
+C<MERGE> normally means that the memoized function does not
+distinguish between list and sclar context, and that return values in
+both contexts should be stored together.  Both C<LIST_CACHE =E<gt>
+MERGE> and C<SCALAR_CACHE =E<gt> MERGE> mean the same thing.
+
+Consider this function:
+
+       sub complicated {
+          # ... time-consuming calculation of $result
+          return $result;
+        }
+
+The C<complicated> function will return the same numeric C<$result>
+regardless of whether it is called in list or in scalar context.
+
+Normally, the following code will result in two calls to C<complicated>, even
+if C<complicated> is memoized:
+
+    $x = complicated(142);
+    ($y) = complicated(142);
+    $z = complicated(142);
+
+The first call will cache the result, say 37, in the scalar cache; the
+second will cach the list C<(37)> in the list cache.  The third call
+doesn't call the real C<complicated> function; it gets the value 37
+from the scalar cache.
+
+Obviously, the second call to C<complicated> is a waste of time, and
+storing its return value is a waste of space.  Specifying C<LIST_CACHE
+=E<gt> MERGE> will make C<memoize> use the same cache for scalar and
+list context return values, so that the second call uses the scalar
+cache that was populated by the first call.  C<complicated> ends up
+being called only once, and both subsequent calls return C<3> from the
+cache, regardless of the calling context.
+
+=head3 List values in scalar context
 
 Consider this function:
 
-       sub pi { 3; }
+    sub iota { return reverse (1..$_[0]) }
+
+This function normally returns a list.  Suppose you memoize it and
+merge the caches:
 
-Normally, the following code will result in two calls to C<pi>:
+    memoize 'iota', SCALAR_CACHE => 'MERGE';
 
-    $x = pi();
-    ($y) = pi();
-    $z = pi();
+    @i7 = iota(7);
+    $i7 = iota(7);
 
-The first call caches the value C<3> in the scalar cache; the second
-caches the list C<(3)> in the list cache.  The third call doesn't call
-the real C<pi> function; it gets the value from the scalar cache.
+Here the first call caches the list (1,2,3,4,5,6,7).  The second call
+does not really make sense. C<Memoize> cannot guess what behavior
+C<iota> should have in scalar context without actually calling it in
+scalar context.  Normally C<Memoize> I<would> call C<iota> in scalar
+context and cache the result, but the C<SCALAR_CACHE =E<gt> 'MERGE'>
+option says not to do that, but to use the cache list-context value
+instead. But it cannot return a list of seven elements in a scalar
+context. In this case C<$i7> will receive the B<first element> of the
+cached list value, namely 7.
 
-Obviously, the second call to C<pi> is a waste of time, and storing
-its return value is a waste of space.  Specifying C<LIST_CACHE =E<gt>
-MERGE> will make C<memoize> use the same cache for scalar and list
-context return values, so that the second call uses the scalar cache
-that was populated by the first call.  C<pi> ends up being called only
-once, and both subsequent calls return C<3> from the cache, regardless
-of the calling context.
+=head3 Merged disk caches
 
 Another use for C<MERGE> is when you want both kinds of return values
 stored in the same disk file; this saves you from having to deal with
@@ -757,7 +784,7 @@ keep the two sets of return values separate.  For example:
        memoize 'myfunc',
          NORMALIZER => 'n',
          SCALAR_CACHE => [HASH => \%cache],
-         LIST_CACHE => MERGE,
+         LIST_CACHE => 'MERGE',
        ;
 
        sub n {
@@ -994,49 +1021,50 @@ C<Memoize>, send an empty note to C<mjd-perl-memoize-request@plover.com>.
 
 Mark-Jason Dominus (C<mjd-perl-memoize+@plover.com>), Plover Systems co.
 
-See the C<Memoize.pm> Page at http://www.plover.com/~mjd/perl/Memoize/
+See the C<Memoize.pm> Page at http://perl.plover.com/Memoize/
 for news and upgrades.  Near this page, at
-http://www.plover.com/~mjd/perl/MiniMemoize/ there is an article about
+http://perl.plover.com/MiniMemoize/ there is an article about
 memoization and about the internals of Memoize that appeared in The
 Perl Journal, issue #13.  (This article is also included in the
 Memoize distribution as `article.html'.)
 
-The author's book I<Higher Order Perl> (2005, ISBN 1558607013, published
-by Morgan Kaufmann) discusses memoization (and many other fascinating
-topics) in tremendous detail. It will also be available on-line for free.
-For more information, visit http://perl.plover.com/book/ .
+The author's book I<Higher-Order Perl> (2005, ISBN 1558607013, published
+by Morgan Kaufmann) discusses memoization (and many other 
+topics) in tremendous detail. It is available on-line for free.
+For more information, visit http://hop.perl.plover.com/ .
 
 To join a mailing list for announcements about C<Memoize>, send an
 empty message to C<mjd-perl-memoize-request@plover.com>.  This mailing
-list is for announcements only and has extremely low traffic---about
+list is for announcements only and has extremely low traffic---fewer than
 two messages per year.
 
 =head1 COPYRIGHT AND LICENSE
 
-Copyright 1998, 1999, 2000, 2001  by Mark Jason Dominus
+Copyright 1998, 1999, 2000, 2001, 2012  by Mark Jason Dominus
 
 This library is free software; you may redistribute it and/or modify
 it under the same terms as Perl itself.
 
 =head1 THANK YOU
 
-Many thanks to Jonathan Roy for bug reports and suggestions, to
-Michael Schwern for other bug reports and patches, to Mike Cariaso for
-helping me to figure out the Right Thing to Do About Expiration, to
-Joshua Gerth, Joshua Chamas, Jonathan Roy (again), Mark D. Anderson,
-and Andrew Johnson for more suggestions about expiration, to Brent
-Powers for the Memoize::ExpireLRU module, to Ariel Scolnicov for
-delightful messages about the Fibonacci function, to Dion Almaer for
-thought-provoking suggestions about the default normalizer, to Walt
-Mankowski and Kurt Starsinic for much help investigating problems
-under threaded Perl, to Alex Dudkevich for reporting the bug in
-prototyped functions and for checking my patch, to Tony Bass for many
-helpful suggestions, to Jonathan Roy (again) for finding a use for
-C<unmemoize()>, to Philippe Verdret for enlightening discussion of
-C<Hook::PrePostCall>, to Nat Torkington for advice I ignored, to Chris
-Nandor for portability advice, to Randal Schwartz for suggesting the
-'C<flush_cache> function, and to Jenda Krynicky for being a light in
-the world.
+Many thanks to Florian Ragwitz for administration and packaging
+assistance, to John Tromp for bug reports, to Jonathan Roy for bug reports
+and suggestions, to Michael Schwern for other bug reports and patches,
+to Mike Cariaso for helping me to figure out the Right Thing to Do
+About Expiration, to Joshua Gerth, Joshua Chamas, Jonathan Roy
+(again), Mark D. Anderson, and Andrew Johnson for more suggestions
+about expiration, to Brent Powers for the Memoize::ExpireLRU module,
+to Ariel Scolnicov for delightful messages about the Fibonacci
+function, to Dion Almaer for thought-provoking suggestions about the
+default normalizer, to Walt Mankowski and Kurt Starsinic for much help
+investigating problems under threaded Perl, to Alex Dudkevich for
+reporting the bug in prototyped functions and for checking my patch,
+to Tony Bass for many helpful suggestions, to Jonathan Roy (again) for
+finding a use for C<unmemoize()>, to Philippe Verdret for enlightening
+discussion of C<Hook::PrePostCall>, to Nat Torkington for advice I
+ignored, to Chris Nandor for portability advice, to Randal Schwartz
+for suggesting the 'C<flush_cache> function, and to Jenda Krynicky for
+being a light in the world.
 
 Special thanks to Jarkko Hietaniemi, the 5.8.0 pumpking, for including
 this module in the core and for his patient and helpful guidance
index 078f69e..cf5f7f5 100644 (file)
@@ -11,7 +11,7 @@ See L<Memoize>.
 =cut
 
 use vars qw(@ISA $VERSION);
-$VERSION = '1.02';
+$VERSION = '1.03';
 @ISA = qw(DB_File GDBM_File Memoize::NDBM_File Memoize::SDBM_File ODBM_File) unless @ISA;
 
 my $verbose = 1;
index 3d6d3e4..9b3b944 100644 (file)
@@ -3,7 +3,7 @@ package Memoize::Expire;
 # require 5.00556;
 use Carp;
 $DEBUG = 0;
-$VERSION = '1.02';
+$VERSION = '1.03';
 
 # This package will implement expiration by prepending a fixed-length header
 # to the font of the cached data.  The format of the header will be:
index e3123b8..06b72f8 100644 (file)
@@ -10,7 +10,7 @@ See L<Memoize::Expire>.
 
 =cut
 
-$VERSION = '1.02';
+$VERSION = '1.03';
 use Carp;
 
 my $Zero = pack("N", 0);
index 3c69e56..7f7dd28 100644 (file)
@@ -18,7 +18,7 @@ to mjd-perl-memoize+@plover.com.
 
 =cut
 
-$VERSION = '1.02';
+$VERSION = '1.03';
 my %cache;
 
 sub TIEHASH {  
index 07b8950..ff934c6 100644 (file)
@@ -12,7 +12,7 @@ See L<Memoize>.
 
 use NDBM_File;
 @ISA = qw(NDBM_File);
-$VERSION = '1.02';
+$VERSION = '1.03';
 
 $Verbose = 0;
 
index 6cb11af..7cfaa4a 100644 (file)
@@ -12,7 +12,7 @@ See L<Memoize>.
 
 use SDBM_File;
 @ISA = qw(SDBM_File);
-$VERSION = '1.02';
+$VERSION = '1.03';
 
 $Verbose = 0;
 
index 33e35b4..1314797 100644 (file)
@@ -11,7 +11,7 @@ See L<Memoize>.
 =cut
 
 use Storable ();
-$VERSION = '1.02';
+$VERSION = '1.03';
 $Verbose = 0;
 
 sub TIEHASH {
index 5968612..2d1d6a6 100644 (file)
@@ -7,7 +7,7 @@
 =item * 
 
 LIST_CACHE doesn't work with ties to most DBM implementations, because
-Memouze tries to save a listref, and DB_File etc. can only store
+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.
 
@@ -329,7 +329,7 @@ the function if performance is bad.
 
 =item *
 
-20010517 Option to have normalizer *modify* @_ for use by memoized
+20010517 Option to have normalizer I<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
index 44847c3..4ad6c96 100644 (file)
@@ -2,6 +2,7 @@
 
 use lib '..';
 use Memoize 'memoize', 'unmemoize';
+use Test::More;
 
 sub reff {
   return [1,2,3];
@@ -12,20 +13,20 @@ sub listf {
   return (1,2,3);
 }
 
-print "1..6\n";
+sub f17 { return 17 }
+
+plan tests => 7;
 
 memoize 'reff', LIST_CACHE => 'MERGE';
-print "ok 1\n";
 memoize 'listf';
-print "ok 2\n";
 
 $s = reff();
 @a = reff();
-print @a == 1 ? "ok 3\n" : "not ok 3\n";
+is(scalar(@a), 1, "reff list context");
 
 $s = listf();
 @a = listf();
-print @a == 3 ? "ok 4\n" : "not ok 4\n";
+is(scalar(@a), 3, "listf list context");
 
 unmemoize 'reff';
 memoize 'reff', LIST_CACHE => 'MERGE';
@@ -34,10 +35,13 @@ memoize 'listf';
 
 @a = reff();
 $s = reff();
-print @a == 1 ? "ok 5\n" : "not ok 5\n";
+is(scalar @a, 1, "reff list context");
 
 @a = listf();
 $s = listf();
-print @a == 3 ? "ok 6\n" : "not ok 6\n";
-
+is(scalar @a, 3, "listf list context");
 
+memoize 'f17', SCALAR_CACHE => 'MERGE';
+is(f17(), 17, "f17 first call");
+is(f17(), 17, "f17 second call");
+is(scalar(f17()), 17, "f17 scalar context call");