DekGenius.com
[ Team LiB ] Previous Section Next Section

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.

    [ Team LiB ] Previous Section Next Section