We have used subroutine references a couple of times in previous articles, but we haven’t taken a look at them directly.
You can take a reference to an existing (named) subroutine using the backslash operator along the ampersand in the following manner:
sub foo {
print "This is foo\n";
}
my $sref = \&foo;
You can call the referenced routine by preceding the scalar holding it with an ampersand, or by using the dereference arrow and parentheses:
&$sref(); # or: &$sref('argument')
$sref->(); # or: $sref->('argument')
It is important to realize that you cannot take a reference to a subroutine and pass it arguments at the same time:
my $sref = \&foo('argument');
What this actually does is return a reference to the return value of that function (using the given argument), which probably isn’t what you wanted.
But what are subroutine references good for? A couple of common uses are dispatch tables and passing routines as arguments to other routines — we will consider the first here. A dispatch table is simply a table (usually a hash) that allows you to select which routine to run. Consider a user interface that queries the user to enter a command to run:
#!/usr/bin/perl -w
use strict;
sub this { print "You picked 'this'\n" }
sub that { print "You picked 'that'\n" }
sub quit { exit }
print "Enter a command:\n";
while (<STDIN>) {
chomp(my $cmd = $_);
if ($cmd eq 'this') {
this();
} elsif ($cmd eq 'that') {
that();
} elsif ($cmd eq 'quit' or $cmd eq 'exit') {
quit();
} else {
print "Unrecognized command: $cmd\n";
}
}
__END__
Now imagine that there are many more possible commands the user could enter and you can see that the program would grow quite large. Using a dispatch table simplifies all of the logic of the if/elsif statements into the datastructure:
#!/usr/bin/perl -w
use strict;
sub this { print "You picked 'this'\n" }
sub that { print "You picked 'that'\n" }
sub quit { exit }
my %dispatch = (
this => \&this,
that => \&that,
quit => \&quit,
exit => \&quit,
);
print "Enter a command:\n";
while (<STDIN>) {
chomp(my $cmd = $_);
if ($dispatch{$cmd}) {
$dispatch{$cmd}->();
} else {
print "Unrecognized command: $cmd\n";
}
}
__END__
This program is actually a little longer, but it is simpler in design and simpler to maintain and update — adding new commands involves only defining a subroutine and adding another entry to the hash (we don’t have to add further elsif clauses as we would with the first example.
*****



