Perl Idioms

There's more than one way to do it.
—the perl(1) man page

Perl borrows from many languages. One consequence of this is that it is easy for programmers to learn: it probably supports some features of languages that they already know. On the other hand, Perl also has powerful constructs that aren't found in other languages.

This page describes some Perl idioms that may not be obvious to programmers coming from other languages.


Hashes as sets

Nominally, a hash maps keys to values. Sometimes, we only care whether a key has a value, not what the value is. Used this way, a hash represents a set:
%humped = { camel     => 1,
            dromedary => 1 };
The elements of the set (keys of the hash) are guaranteed unique. To test whether an element is in the set, write
$humped{$animal} or die "$animal has no humps\n";

map & grep

Perl provides the usual explicit looping constructs: for, while, and until. It also provides two implicit looping constructs: map and grep.

Explicit loops are preferred when the loop body is executed for its side effects:

foreach $animal (@animals) { print "$animal\n" }
Implicit loops are preferred when the loop body is executed for its value:
@humped = grep { $humped{$_} } @animals;
Combining map with the hash construct described above yields the unique elements of a list:
%unique = map { $_ => 1 } @list;
@unique = keys %unique;
Be agressive with map. Any time you see
for (@a)
{
    push @b, ...
}
consider replacing it with
@b = map { ... } @a;

and, or, & not

Perl has the usual block-oriented control structures:
if (expression)
{
    statement
}
elsif (expression)
{
    statement
}
else
{
    statement
}
It also has statement-oriented control structures:
statement if     expression;
statement unless expression;
and expression-oriented control structures:
expression and expression;
expression or  expression;
Any time you find yourself using if to control a block of code, consider whether you couldn't do the same thing more simply using and or or to control an expression:
if (open(FOO, "foo") 
{ 
    ... 
} 
else 
{ 
    die Can't open "foo: $!\n";
}
becomes
open(FOO, "foo") or die "Can't open foo: $!\n";

for as switch

Perl has no official switch statement, and the constructs suggested in Programming Perl can be ungainly, especially if the switch variable isn't already assigned to $_. The for loop will do this assignment, and provide a block for the cases to exit from, as well:
for ($animal)
{
    /camel/     and do Humps(2), last;
    /dromedary/ and do Humps(1), last;
}

Return an array or an array reference

It is more efficient to return an array reference than an actual array. Looking up elements via a reference is easy:
$val = $object->method->[3]
but using the same reference in a for loop is awkward:
for (@{$object->method})
If a class itself doesn't dictate one approach or the other on conceptual grounds, let the user choose:
sub method
{
    ...
    wantarray ? @array : \@array
}
Then the for loop becomes
for ($object->method)
while a user who cares about efficiency can still write
$arrayref = $object->method;

Parsing command line flags

Use Getopt::Std to parse command line flags. You can set default values by assigning to the %Options hash before calling getopt():
$Options{c} = 'blue';
getopt('c', \%Options);
$color = $Options{c};

Slurping a file

One way to read a whole file in at once is to undef $/.
undef $/;
$everything = <FH>
However, all filehandles use the same value of $/, so you have to restore its previous value afterwards, or risk problems with other files. If you localize $/ inside a block (or a subroutine), then its previous value is automatically restored when you exit the block.
{
local $/;
undef $/;
$everything = <FH1>
}

$something = <FH2>

Alternately, you can join all the lines on null:

$everything = join('', <FH>);
For a list of all the words in a files, write:
@words = split(' ', join('' <FH>));

Steven W. McDougall / resume / swmcd@world.std.com / 1999 November 15