closures

Repository for my Perl files that document how to use closures
git clone git://git.samirparikh.com/closures
Log | Files | Refs

closures.pl (3446B) - raw


      1 #!/usr/local/bin/perl
      2 
      3 use strict;
      4 use warnings;
      5 use v5.32;
      6 
      7 # From Chapter 7 of "Beginning Perl" by Curis "Ovic" Poe:
      8 # The make_fibonacci() subroutine returns an anonymous subroutine that
      9 # references the $current and $next variables declared in the
     10 # make_fibonacci() subroutine, but outside of the anonymous subroutine.
     11 # The $iterator variable contains a reference to this anonymous subroutine,
     12 # and it “remembers” the values of the $current and $next variables. Every
     13 # time it is invoked, it updates the values of $current and $next and
     14 # returns the next Fibonacci number. Eventually, you get to the for loop
     15 # that prints the first 10 Fibonacci numbers. You can pass the $iterator
     16 # variable to other subroutines just like any other variable, and it still
     17 # remembers its state. You can create several iterators with this same
     18 # subroutine, and each will have a separate copy of $current and $next.
     19 
     20 sub make_fibonacci {
     21     my ( $current, $next ) = ( 0, 1 );
     22     return sub {
     23         my $fibonacci = $current;
     24         ( $current, $next ) = ( $next, $current + $next );
     25         return $fibonacci;
     26     }
     27 }
     28 
     29 my $iterator = make_fibonacci();
     30 
     31 for ( 1 .. 10 ) {
     32     my $fibonacci = $iterator->();
     33     say $fibonacci;
     34 } # 0, 1, 1, 2, ..., 21, 34
     35 
     36 say $iterator->(); # 55
     37 
     38 say "--------------------";
     39 
     40 my $iterator2 = make_fibonacci();
     41 say $iterator2->() for ( 1 .. 5 ); # 0, 1, 1, 2, 3
     42 
     43 say "--------------------";
     44 
     45 # The example below is taken from Chapter 2 of "Object Oriented Perl" by
     46 # Damian Conway.
     47 # When the hop_along subroutine is called, it copies its three arguments
     48 # into lexical variables, sets up another lexical (i.e., $next) as a counter,
     49 # and then creates an anonymous sub-routine that uses the values in those
     50 # variables. The anonymous subroutine increments $next by $step each time
     51 # the subroutine is called, until the result is greater than $to. A reference
     52 # to the newly created anonymous subroutine is then returned.
     53 
     54 sub hop_along {
     55     my ( $from, $to, $step ) = @_;       # unpack arguments
     56     my $next = $from - $step;            # initialize counter
     57     my $closure_ref = 
     58         sub {
     59 
     60 # the first time we call the iterator $closure_ref, $next is equal to
     61 # $from - $step as defined above.  In subsequent calls, increments according
     62 # to $next += $step.
     63             $next += $step;              # take a step
     64             return if ( $next > $to );   # we've reached the end
     65 # the next line sets the calling parameter $_[0] to equal to the $next that
     66 # is in the scope of this array.  $_[0] is set as the calling parameter
     67 # which is transferred to the $next down below in the calling routine.
     68             $_[0] = $next;             # otherwise, set new value
     69             return 1;                    # exit
     70         };
     71     return $closure_ref;                 # return closure
     72 }
     73 
     74 # Normally, the lexical variables inside the call to hop_along (i.e., $from,
     75 # $to, $step, $next) would cease to exist when hop_along returned. But,
     76 # because the anonymous subroutine still needs access to them, Perl arranges
     77 # for them to live on in seclusion, accessible only by the anonymous
     78 # subroutine itself. Closures are a means of giving a subroutine its own
     79 # private memory — variables that persist between calls to that subroutine
     80 # and are accessible only to it.
     81 
     82 my $iterator3 = hop_along (1, 30, 7);    # Create closure
     83 my $next;
     84 while ($iterator3->($next))              # Call closure
     85 {
     86     say $next;
     87 }