Perl Circus - Three Rings of Perl Tricks.

Subroutines

from Learning Perl... We've already seen and used some of the builtin system functions, such as chomp, reverse, print, and so on. But, as other languages do, Perl has the ability to make subroutines, which are user-defined functions. These let us recycle one chunk of code many times in one program.

To define your own subroutine, use the keyword sub, the name of the subroutine (without the ampersand), then the indented block of code (in curly braces) which makes up the body of the subroutine, something like this:

sub marine {
    $n += 1;  # Global variable $n
    print "Hello, sailor number $n!\n";
}

Call...

Call a subroutine and pass a variable by value

$value = "foo";
$newvalue = add_bar($value);
print "$value becomes $newvalue";

sub add_bar {
    my ($copy) = @_;
    $copy .= "bar";
    return $copy;
}
foo becomes foobar

In any Perl subroutine the special @_ array will always contain aliases to the variables that are passed in, so it really isn't possible to pass by value. The trap to avoid is that a subroutine which operates directly on the @_ elements will actually change the variables aliased by those elements. If you wish to operate only on the values you must copy the @_ values into local variables, operate on those local variables and then return the new result. This will leave the original variable unchanged.

Call a subroutine and pass a variable by reference

$value = "foo";
add_bar($value);
print "$value";

sub add_bar {
    @_[0] .= "bar";
}
foobar

In contrast to the previous trick this one takes advantage of Perl's default behavior of passing aliases, to change the original variable more directly. The @_ array is sometimes described as "magical" but it is really no different from other languages which pass arguments by reference. There is no need to return the value since the original variable is directly changed as the subroutine operates on its alias.

Call a subroutine and pass a variable by reference explicitly

$value = "foo";

add_bar(\$value);
print "$value";

sub add_bar {
    my ($ref) = @_;
    $$ref .= "bar";
}
foobar

If you prefer to pass your references more explicitly Perl allows you to use the "\" symbol to reference a variable (such as \$, \@, \&, or \%). The reference can be stored in a scalar just like any other value. Dereference that reference back into the original variable within the subroutine.

Call a subroutine and pass a filehandle

open FH, "/us/docs/independence.txt";
printfile(*FH);

sub printfile {
    my $handle = shift;
    while (<$handle>) {print};
}
When, in the course of human events...

Unlike the last trick, creating a reference to a filehandle is not so obvious since filehandles do not look like ordinary variables. Whereas most variables names are preceded with a special punctuation character which indicates the variables "type" ($ means scalar, for example) filehandles seem to be missing this characteristic. Perl's typeglob comes to the rescue. The special * ("star") type symbol allows you to refer to filehandles as if they were scalars.

Call a subroutine and pass arguments by name

print_many(text=>'hooray', times=>3);

sub print_many {
    my %arg = @_;
    while ($arg{times}--) { print "$arg{text} " }
}
hooray hooray hooray

You can pass a hash to your subroutine, and thereby name your arguments. Perl, however will still place your arguments into the @_ array so you must coerce that array back into a hash by assigning it to a local hash variable.

Call a subroutine and use default values as arguments

print_many(text=>'hooray');

sub print_many {
    my %arg = (text=>"", times=>1, @_);
    while ($arg{times}--) {print "$arg{text} "};
}
hooray

Particularly when using named arguments it is helpful for a subroutine to allow for the caller to omit some values. In these cases your subroutine can use default values that are then overwritten by the actual passed values (if any).

Call a subroutine embedded a in double-quoted string

print "Page requested at <b>@{[get_time()]}</b>";

sub get_time {
    return scalar(gmtime);
}
Page requested at <b>Fri Nov 10 01:59:08 2006</b>

One of Perl's most powerful features is its ability to interpolate variables within double-quoted strings. This easily allows you to print out the values of scalars, arrays or hashes embedded within double-quoted strings like this: "Tonight at the Apollo: $show_name playing at @show_times." Unfortunately this won't work with subroutines. You could store the result of the subroutine call in a temporary variable or break your string up, but a cleaner (although odd-looking) solution is the @{[ ]} wrapper. This creates an array ref containing the sub call, and the casts it back into an array, something that can be printed from within a duoble-quotes string.

Return...

Return multiple values from a subroutine

($ret1, $ret2) = add_bar("foo", "fez");
print "$ret1 and $ret2";

sub add_bar {
    my @ret;
    foreach (@_) {push @ret, $_."bar"}
    return @ret;
}
foobar and fezbar

Subroutines can be very flexible about the number of arguments received and the number of values returned. This is due to the fact that arguments are passed in using an array, and the fact that Perl can return an arbitrarily long list of results.

Return a value from a subroutine based on calling context

($ret1, $ret2) = add_bar("foo", "fez");
$ret3 = add_bar("fee", "foz");
print "$ret1 and $ret2, $ret3";

sub add_bar {
    my @ret;
    foreach (@_) {push @ret, $_."bar"}
    wantarray()? return @ret : return "@ret";
}
foobar and fezbar, feebar fozbar

Using the wantarray function it is possible for a subroutine to detect if the caller is expecting to assign the results to an array, or a scalar variable. This allows the sub to alter its return value for each case. In this example the sub coerces its array into a string if the caller is assigning the result to a scalar. How's that for flexible?

Return an "error" object

my $result = makeGreeting('no one');

if (ref $result eq 'ERROR') {
    die $result->{message};
}

print $result;

sub makeGreeting {
    if ($_[0] eq 'no one') {
    	return bless {
    	    message => "Can't greet no one."
    	}, 'ERROR';
    }
    
    return "Hello $_[0].";
}
Can't greet no one. at untitled text 4 line 4.

There are various ways of signaling a caller that something in the subroutine has gone wrong. One approach is to return nothing on an error, or return a true value, when the subroutine would normally return nothing. The logic can get a little convoluted. Yet another way, one that allows you to return information about what went wrong, is to create an "error object." This is just a hash reference blessed with a tag like 'ERROR'. The ref function will get back whatever tag you've blessed something with, so you can check if the result was a good one or a bad one. Obviously you can bless your hash with whatever tag you want, maybe "OOPSIE" or "UH OH"? And you can put whatever information you want in the hash. SEE ALSO: Shlomi Fish's Error module.

Create...

Create a subroutine reference

$say_hi = sub {
    my $to_whom = shift;
    print "Hello, $to_whom.\n";
};

&$say_hi("Ada");
Hello, Ada.

Normally when a subroutine is declared with the sub keyword the next thing you expect to see is the subroutine's name. In this case however a name is unnecessary since all we really want is to store a reference to this subroutine in a scalar (which already has a name). This trick is known as creating an anonymous subroutine. The scalar reference can be easily coerced back into its original subroutine by prepending it with the "&" symbol. Take heed of the required semicolon after the anonymous sub block. That semicolon is necessary to end the assignment statement, which, in spite of the formatting is what that "line" of code is really doing.

Create subroutines on-the-fly

$to_do = "print uc";
$greeting = "Hello, Charles!";
eval("$to_do \"$greeting\"");
print $@ if ($@);
HELLO, CHARLES!

Using the eval function it is possible to execute a string as if it were a code block. Here the print and upper-case functions are run on the quoted string just as if they were part of the program itself. Unlike regular code blocks however eval will not kill the entire program if you give it incorrect code. Instead it will silently store its complaints in the special $@ variable, which can then be checked afterwards.

Create a subroutine that overrides a built-in function

use subs 'int';

sub int{
    my $arg = shift;
    return CORE::int($arg)*10;
}
print int("5.7");
50

Perl's built-in subroutines, like int can be redefined with your own subroutines if you wish. Normally int would return the integer portion of a number (in this case 5), but for some reason I wish to return the integer times ten. In order to avoid a recursive call back to my own subroutine I first call the original int in the CORE package, and then multiply that by 10 before returning the final result. You could have accomplished the same thing without using "int" as the name of your subroutine -- "int_times_10" might seem like a good idea, but this trick would be just the what you need to easily dynamically alter existing functionality at runtime.