Siaris
Simple Things
Syndicate: full/short
Siaris
Categories
General0
News2
Programming2
LanguageBits0
Perl50
Ruby10
VersionControl1
Misc1
Article Calendar
<= May, 2008
S M T W T F S
123
45678910
11121314151617
18192021222324
25262728293031
Search this blog

Key links
External Blogs
Brought to you by ...
Ruby
1and1.com

Subroutine References

Andrew L. Johnson (First published by ItWorld.com 2001-08-16)

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.

*****