Subroutine References

By Andrew Johnson, ITworld |  How-to

We have used subroutine references a couple of times in previous
articles, but we haven't looked 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 the 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 () {
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 data structure:

#!/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 () {
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.

Next Week: Passing subroutine references

Join us:
Facebook

Twitter

Pinterest

Tumblr

LinkedIn

Google+

Answers - Powered by ITworld

ITworld Answers helps you solve problems and share expertise. Ask a question or take a crack at answering the new questions below.

Join us:
Facebook

Twitter

Pinterest

Tumblr

LinkedIn

Google+

Ask a Question
randomness