First, you must understand that named subroutines are global by nature;
that is, the subroutine name (identifier) lives in the package's symbol
table. However, they run within the lexical scope they were defined
within.
#!/usr/bin/perl -w
use strict;
{
my $inc = 10;
sub incr {
print "$inc\n";
$inc++;
}
}
incr();
incr();
__END__
prints:
10
11
In the above example, $inc is lexically scoped within the block, and
the subroutine (incr()) is defined within the same block. We cannot
refer to $inc after the block (scope) is finished but we can call the
subroutine, which runs within its defined scope so it can still access
and change $inc. A closure is simply an anonymous subroutine bound to
the lexical scope it was defined within (rather like the above
situation).
#!/usr/bin/perl -w
use strict;
sub make_incr {
my $inc = shift;
return sub {print "$inc\n"; $inc++};
}
my $c1 = make_incr(10);
my $c2 = make_incr(20);
$c1->();
$c2->();
$c1->();
$c2->();
__END__
prints:
10
20
11
21
Our make_incr() routine is a closure generator. It takes an argument
and creates a new lexical variable, then it returns an anonymous
subroutine that uses this same variable. Each time we call this
routine, a new lexical $inc is created and the anonymous subroutine
refers to that particular copy of $inc. Each closure generated ($c1 and
$c2) works with their own private copies of $inc. Essentially, they
each can save information about their current state (in this case, what
number they are currently at).
Here is another simple example (adapted from my book) where the closure
itself also takes an argument:
sub exclaim {
my $prefix = shift;
return sub {print "$prefix $_[0]!\n"};
}
my $batman = exclaim('Indeed');
my $robin = exclaim('Holy');
$robin->('Mackerel'); # prints: Holy Mackerel!
$batman->('Robin'); # prints: Indeed Robin!
Closures can be useful for creating callback routines and iterators.
The above example of make_incr() creates very simple iterators that
simply count up by ones from a given base number. However, you can
create an iterator to iterate through more complex functions as well.
Next week we will discuss using a closure as an iterator over the
Fibonacci numbers.
Next Week: Closures, Part 2: Iterating Over the Fibonacci Numbers