6.7 Closure Variables as Static Local Variables
A subroutine doesn't
have to be an anonymous subroutine to be a closure. If a named
subroutine accesses lexical variables and those variables go out of
scope, the named subroutine retains a reference to the lexicals, just
as you saw with anonymous subroutines. For example, consider two
routines that count coconuts for Gilligan:
{
my $count;
sub count_one { ++$count }
sub count_so_far { return $count }
}
If you place this code at the beginning of
the program, the variable $count is declared, and
the two subroutines that reference the variable become closures.
However, because they have a name, they will persist beyond the end
of the scope (as do all named subroutines). Since the subroutines
persist beyond the scope and access variables declared within that
scope, they become closures and thus can continue to access
$count throughout the lifetime of the program.
So, with a few calls, you can see an incremented count:
count_one( );
count_one( );
count_one( );
print "we have seen ", count_so_far( ), " coconuts!\n";
$count retains its value between calls to
count_one( ) or count_so_far(
), but no other section of code can access this
$count at all.
In C, this is known as a static local variable:
a variable that is visible to only a subset of the
program's subroutines but persists throughout the
life of the program, even between calls to those subroutines.
What if you wanted to count down? Something like this will do:
{
my $countdown = 10;
sub count_down { $countdown-- }
sub count_remaining { $countdown }
}
count_down( );
count_down( );
count_down( );
print "we're down to ", count_remaining( ), " coconuts!\n";
That is, it'll do as long as you put it near the
beginning of the program, before any invocations of
count_down( ) or count_remaining(
). Why?
This block doesn't work when you put it after those
invocations because there are two functional parts to the first line:
my $countdown = 10;
One part is the declaration of
$countdown as a lexical variable. That part is
noticed and processed as the program is parsed during the
compile phase. The second part is the assignment
of 10 to the allocated storage. This is handled as the code is
executed during the run phase. Unless the run
phase is executed for this code, the variable has its initial
undef value.
One
practical solution to this problem is to change the block in which
the static local appears into a BEGIN block:
BEGIN {
my $countdown = 10;
sub count_down { $countdown-- }
sub count_remaining { $countdown }
}
The BEGIN keyword
tells the Perl compiler that as soon as this block has been parsed
successfully (during the compile phase), jump for a moment to run
phase and run the block as well. Presuming the block
doesn't cause a fatal error, compilation then
continues with the text following the block. The block itself is also
discarded, ensuring that the code within is executed precisely once
in a program, even if it had appeared syntactically within a loop or
subroutine.
|