commit 14b04b972af71aa5ec91adb6ad90a3720471fbc0
Author: Samir Parikh <noreply@samirparikh.com>
Date: Fri, 9 Dec 2022 14:25:08 +0000
initial commit
Diffstat:
A | closures.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;
+}