closures

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

commit 14b04b972af71aa5ec91adb6ad90a3720471fbc0
Author: Samir Parikh <noreply@samirparikh.com>
Date:   Fri,  9 Dec 2022 14:25:08 +0000

initial commit

Diffstat:
Aclosures.pl | 87+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 87 insertions(+), 0 deletions(-)

diff --git a/closures.pl b/closures.pl @@ -0,0 +1,87 @@ +#!/usr/local/bin/perl + +use strict; +use warnings; +use v5.32; + +# From Chapter 7 of "Beginning Perl" by Curis "Ovic" Poe: +# The make_fibonacci() subroutine returns an anonymous subroutine that +# references the $current and $next variables declared in the +# make_fibonacci() subroutine, but outside of the anonymous subroutine. +# The $iterator variable contains a reference to this anonymous subroutine, +# and it “remembers” the values of the $current and $next variables. Every +# time it is invoked, it updates the values of $current and $next and +# returns the next Fibonacci number. Eventually, you get to the for loop +# that prints the first 10 Fibonacci numbers. You can pass the $iterator +# variable to other subroutines just like any other variable, and it still +# remembers its state. You can create several iterators with this same +# subroutine, and each will have a separate copy of $current and $next. + +sub make_fibonacci { + my ( $current, $next ) = ( 0, 1 ); + return sub { + my $fibonacci = $current; + ( $current, $next ) = ( $next, $current + $next ); + return $fibonacci; + } +} + +my $iterator = make_fibonacci(); + +for ( 1 .. 10 ) { + my $fibonacci = $iterator->(); + say $fibonacci; +} # 0, 1, 1, 2, ..., 21, 34 + +say $iterator->(); # 55 + +say "--------------------"; + +my $iterator2 = make_fibonacci(); +say $iterator2->() for ( 1 .. 5 ); # 0, 1, 1, 2, 3 + +say "--------------------"; + +# The example below is taken from Chapter 2 of "Object Oriented Perl" by +# Damian Conway. +# When the hop_along subroutine is called, it copies its three arguments +# into lexical variables, sets up another lexical (i.e., $next) as a counter, +# and then creates an anonymous sub-routine that uses the values in those +# variables. The anonymous subroutine increments $next by $step each time +# the subroutine is called, until the result is greater than $to. A reference +# to the newly created anonymous subroutine is then returned. + +sub hop_along { + my ( $from, $to, $step ) = @_; # unpack arguments + my $next = $from - $step; # initialize counter + my $closure_ref = + sub { + +# the first time we call the iterator $closure_ref, $next is equal to +# $from - $step as defined above. In subsequent calls, increments according +# to $next += $step. + $next += $step; # take a step + return if ( $next > $to ); # we've reached the end +# the next line sets the calling parameter $_[0] to equal to the $next that +# is in the scope of this array. $_[0] is set as the calling parameter +# which is transferred to the $next down below in the calling routine. + $_[0] = $next; # otherwise, set new value + return 1; # exit + }; + return $closure_ref; # return closure +} + +# Normally, the lexical variables inside the call to hop_along (i.e., $from, +# $to, $step, $next) would cease to exist when hop_along returned. But, +# because the anonymous subroutine still needs access to them, Perl arranges +# for them to live on in seclusion, accessible only by the anonymous +# subroutine itself. Closures are a means of giving a subroutine its own +# private memory — variables that persist between calls to that subroutine +# and are accessible only to it. + +my $iterator3 = hop_along (1, 30, 7); # Create closure +my $next; +while ($iterator3->($next)) # Call closure +{ + say $next; +}