Closures are used in two somewhat distinct ways. The most common usage is as “smart” callback procedures. The other idiom is that of “iterators” (or “streams,” as they are known in the LISP world).
Since closures are subroutine references with a bit of private data thrown in, they are very convenient to use as callback procedures in graphical user interfaces.
Let’s say you create a button using the Tk toolkit and give it a subroutine reference. When the button is pressed, it calls this subroutine back. Now if the same subroutine is given to two different buttons on the screen, there’s a problem: How does the subroutine know which button is calling it? Simple. Instead of giving the button a reference to an ordinary subroutine, you give it a “smart” callback subroutine—a closure. This closure stores away some data specific to a button (such as its name), and when the subroutine is called, it magically has access to that data, as shown in Example 4.2.
This example creates two buttons that when clicked, print out their title strings. Though the discussion about packages and, specifically, the Tk module is slated for chapters to come, you might still understand the gist of the code in Example 4.2. For the moment, pay attention only to the part that uses closures (highlighted in boldface) and not to the mechanics of using the Tk module.
CreateButton
creates a GUI button and feeds it a
reference to an anonymous subroutine reference
($callback_proc
), which holds on to
$title
, a my
variable in its
enclosing environment. When the user clicks on the button, the
callback is invoked, whereupon it uses its stored value of
$title
.
Example 4-2. Passing Closures Instead of Ordinary Subroutines
use Tk; # Creates a top level window $topwindow = MainWindow->new(); # Create two buttons. The buttons print their names when clicked on. CreateButton($topwindow, "hello"); CreateButton($topwindow, "world"); Tk::MainLoop(); # Dispatch events. #-------------------------------------------------------------------- sub CreateButton { my ($parent, $title) = @_; my($b); $callback_proc = sub { print "Button $title pressed\n"; }; $b = $parent->Button( '-text' => "$title", # Button title '-fg' => 'red', # foreground color '-command' => $callback_proc # sub to call when the button # is pressed ); $b->pack(); }
Note that the buttons couldn’t care less whether they get references to ordinary subroutines or closures.
An iterator keeps track of where it currently is in a “stream” of entities and returns the next logical entity every time it is called. It is like a database cursor, which returns the next record from a stream of records (the list of records that match the given query). A stream can be bounded (a set of records from a database) or unbounded (a stream of even numbers).
Let’s take a look at how closures can be used to represent streams and iterators. The first example is a stream of even numbers and an iterator on this stream that returns the next even number whenever it is called. Clearly, we cannot generate all possible even numbers (as in the bounded case), but we can always compute the next even number if we remember the previous number generated. The iterator remembers this crucial piece of information.
Subroutine even_number_printer_gen
takes an
integer and returns a subroutine that prints even numbers starting
from the given integer.[21] This program is shown in Example 4.3.
Example 4-3. An Even Number Stream Generator
sub even_number_printer_gen { # This function returns a reference to an anon. subroutine. # This anon. subroutine prints even numbers starting from $input. my($input) = @_; if ($input % 2) { $input++}; # Next even number, if the given # number is odd my $rs = sub { print "$input "; # Using $input,which is a my variable # declared in an outside scope $input += 2; }; return $rs; # Return a reference to the subroutine above }
And now for its usage:
# We want even numbers starting from 30. Ask even_number_printer_gen # for a customized iterator that can do such a thing. $iterator = even_number_printer_gen(30); # $iterator now points to a closure. # Every time you call it, it prints the next successive even number. for ($i = 0; $i < 10; $i++) { &$iterator(); } print "\n";
This prints
30 32 34 36 38 40 42 44 46 48
$iterator
holds on to $input
and uses it as private storage subsequently, storing the last even
number. Of course, you can create as many iterators as you want, each
primed with its own starting number:
$iterator1 = even_number_print_gen (102); $iterator2 = even_number_print_gen (22); &$iterator1(); # Prints 102 &$iterator2(); # Prints 22 &$iterator1(); # Prints 104 &$iterator2(); # Prints 24
Notice how each subroutine reference is using its own private value
for $input
.
Can two closures share the same variables? Sure, as long as they are created in the same environment. Example 4.4 produces two anonymous functions, one that prints even numbers and another that prints odd numbers. Each of these functions prints out the even (or odd) number after the number last printed (by either function), regardless of how many times either of them is called in succession.
Example 4-4. Closures Sharing Variables
sub even_odd_print_gen {
# $last is shared between the two procedures
my ($rs1, $rs2);
my ($last) = shift; # Shared by the two closures below
$rs1 = sub { # Even number printer
if ($last % 2) {$last ++;}
else { $last += 2};
print "$last \n";
};
$rs2 = sub { # Odd number printer
if ($last % 2) {$last += 2 }
else { $last++};
print "$last \n";
};
return ($rs1, $rs2); # Returning two anon sub references
}
($even_iter,$odd_iter) = even_odd_print_gen(10);
&$even_iter (); # prints 12
&$odd_iter (); # prints 13
&$odd_iter (); # prints 15
&$even_iter (); # prints 16
&$odd_iter (); # prints 17
This example takes advantage of the fact that Perl can return
multiple values from one subroutine, so there is no problem returning
references to two anonymous subroutines, both of which happen to be
referring to $last
. You can call
even_odd_print_gen
as many times as you want with
different seeds, and it keeps returning pairs of subroutine closures.
The important point is that to share the same data, the anonymous
subroutines must have been created in the same scope. This example
also highlights the fact that a closure truly hangs onto the
my
variables it needs instead of copying or
interpolating the variable’s values.
Let’s turn our attention to a more useful example of an unbounded stream, that of a stream of random numbers. The strategy is identical to that used in the previous example: the iterator keeps track of the last generated pseudo-random number.
You might argue that the
rand()
function represents an iterator primed
with a seed (using srand
). You are right. But
let’s say you want to write a simulation program that depends
on two independent sources of random number
generation. Using rand
in both these sources does
not make them independent; the reason is that rand
happens to calculate a new random number based on the last number it
generated (it stores it in a global variable), and calling
rand
for one stream affects the next number
retrieved by the other stream.
Closures provide a nice solution, because they are a combination of
code and private data. Instead of using
srand
, we’ll use the function
my_srand
, which returns a random-number-generating
subroutine, seeded with an appropriate initial value. In other words,
my_srand
is a “generator of random number
generators” that returns a custom anonymous subroutine, primed
with an initial value for $rand
.
In the implementation in Example 4.5, please don’t pay too much attention to the algorithm itself (the linear congruential method), because the randomness due to the particular constants chosen has not been tested (it also repeats every 1,000 numbers). Besides, there are much better algorithms.
Example 4-5. A Random-Number-Generating Stream
sub my_srand { my ($seed) = @_; # Returns a random number generator function # Being predictive, the algorithm requires you to supply a # random initial value. my $rand = $seed; return sub { # Compute a new pseudo-random number based on its old value # This number is constrained between 0 and 1000. $rand = ($rand*21+1)%1000; }; }
We can now use my_srand
as many times as we want
and get back completely independent closures, each capable of
generating random numbers from its own starting point:
&$random_iter1 = my_srand (100); &$random_iter2 = my_srand (1099); for ($i = 0; $i < 100; $i++) { print &$random_iter1(), " ", &$random_iter2(), "\n"; }
If you don’t have a background in object orientation, you might be able to understand this section better after you have read Section 7.2.
An object, to give the street definition, is a package of data and
functions. The data provides the context for the object’s
functions to work properly. When you say, for example,
$button->setForeground("yellow")
, the
setForeground
function automatically knows which
button you are talking about.
In a sense, the facility for closures attempts the same
feature—it is also a binding between a subroutine and some
private data that is available only to that subroutine. As we saw
earlier, in the even_odd_print_gen
example, there
can be any number of subroutines that can refer to the same basic
data, as long as they were all created in exactly the same scope.
Abelson, Sussman, and Sussman’s delightful Structure and Interpretation of Computer Programs [2] illustrates
how to create and use such objects in Scheme (a LISP dialect).
Perl supports a number of features for object orientation (such as inheritance and virtual functions à la C++) that make it easier to create iterators and streams in an object-oriented style than by using closures (the object’s attributes reflect the “state” of the iterator). Closures are also much more space-intensive than objects but a trifle faster; we will study the reason for this in Chapter 20.
I prefer objects to closures in all cases except one:
callback procedures. I find it easier to
implement callbacks with simple closures than to create
“callback objects,” as you might typically do in C++ (and
have to, in Java). In the
CreateButton
example above, you could create a
callback object with exactly one “method,” say,
execute()
. The button would call the method
$callback_object->execute()
when it was clicked
upon, and the execute
method of that object would
know exactly what to do. The callback object can store all the
context for execute
to work. Instead of all this
work, it is simpler and more direct to use closures, because they
automatically squirrel away the required context.
Tom Christiansen’s
perltoot
document (toot
stands for Tom’s Object-Oriented Tutorial [Chapter 4]) implements objects
using closures to represent the objects’
state. It is an interesting approach, but I don’t use it
because there are simpler approaches for obtaining privacy; besides,
they are faster too. More on this in Chapter 7.
[21] This example and explanation are based on Robert Wilensky’s excellent book LISPcraft (W.W. Norton and Co.).
Get Advanced Perl Programming now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.