PrevIndexNext

Syntactic Sugar

Perl offers many ways of making your programs more concise. Such conciseness may cause a dramatic increase in the 'cryptic coefficient', however! :) This is not good.

Once you learn these idioms of the language they can actually be much clearer than the more long-winded circuitous manner of writing. You can write code that is more to the point and direct. Brevity is the soul of wit.

Michaelangelo is purported to have said:

    The essence of Art is the expurgation of the superfluous.

List Literals

Instead of:

my @names = ("one", "two", "three", "four");
use this:
my @names = qw(one two three four);
You save all kinds of pesky punctuation! Here is another way of writing it which makes it easy to add or delete elements:
my @names = qw/ one two three four /;
For the delimiter after the 'qw' you can use any non alphanumeric character. (), {}, [], <> can be used in pairs.
my @names = qw{ one two three four };
You can't have elements with embedded spaces.

unless and until

Instead of negating the boolean of an if statement you can use the keyword 'unless' instead. These two are equivalent:
if (not ($a < $b)) { print "hello\n"; } unless ($a < $b) { print "hello\n"; }
Similarily there is 'until':
while ($i <= 10) { ... } until ($i > 10) { ... }
Depending on how the boolean is worded these can sometimes be clearer. 'unless' and 'until' are not as commonly used in English as 'if' or 'while'. Avoid 'unless' if the boolean is complex or contains negative terms. Try to not confuse yourself or others!

Statement Modifiers

Remember when we said that even when there is only one statement in the body of an 'if' statement, still the curly braces are required? Statement modifiers to the rescue! Rather than this:
if ($i > 10) { print "lots!\n"; }
You can say this:
print "lots!\n" if $i > 10;
Using the new syntax saved 8 troublesome keystrokes!

Modifiers can also be used with qw/while for unless until/. English has such modifiers so their usage can sound quite natural:

take_out_garbage if you_love_me; add_salt until enough;
A common use of such statement modifiers is with 'last' and 'next':
next if substr($line, 0, 1) eq "#"; last if $i > 10;

The totally cryptic $_ - the default variable

This piece of code:

while (my $line = <IN>) { if (length($line) < 10) { print $line; } }
can be written more concisely:
while (<IN>) { print if length() < 10; }
The lines are being read into the variable $_ and it serves as the 'default' variable for length and print. If you do not provide a parameter for these two functions (and several others) they will assume that you are talking about the variable named $_. It is like a pronoun such as 'it' or 'he'. It is assumed that you know who you are talking about based on context.
    The janitor picked up the broom.
    He pushed it for the next hour.
This can make for clean and tidy programs that are concise and clear (sometimes!).

Shift

The 'shift' function that operates on arrays (by shifting off the first element of the array) has a default argument.

WITHIN a subroutine
the array defaults to @_ - the array holding the parameters to the routine.

OUTSIDE of a subroutine
the array defaults to @ARGV - the array holding the command line parameters.

die "usage: hello name age\n" unless @ARGV == 2; my $name = shift; my $age = shift; sub ask { print shift; my $ans = <STDIN>; chomp $ans; return $ans eq 'y' || (shift && $ans eq 'Y'); } print "You are $age years old!\n" if ask "$name - Are you sure? ", 1;
The code above is rather confusing and could be termed a 'victim' of overly concise, cryptic, and obfuscating syntactic sugar. Here is an equivalent version that is a much better example of good, readable, modifiable code that is easy to maintain:
# # the command 'hello' has two command line parameters: # # - a name # - an age # # Ask the person if they are sure # and if they are, print "You are $age years old!". # if (@ARGV != 2) { die "usage: hello name age\n" } my ($name, $age) = @ARGV; # # Ask a question, get an answer, # and return true if the answer was affirmative (y or Y) # otherwise return false. # # The subroutine has two parameters: # # - a question to ask (preferably without a newline ending) # - a boolean value to indicate whether # the answer can be a capital Y # and not only a lower case y. # # Note that an answer of 'yes' will not be accepted as true. # Also note that in Perl the digit 1 is true and 0 is false. # sub ask { my ($question, $UPPER_Y_is okay) = @_; print $question; my $answer = ; chomp $answer; if ($answer eq 'y' || ($UPPER_Y_is_okay && $answer eq 'Y') ) { return 1; } else { return 0; } } if (ask("$name - Are you sure? ", 1)) { print "You are $age years old!\n"; }
Note the formatting of an if statement when the boolean is complex and multi-lined.

For Statement

The for loop over an array can omit the loop variable. It will default to using $_.
for (@names) { $tot += length; }
and since there is only one statement in the body of the loop the above can be made even more concise:
$tot += length for @names;

Sort Subroutine Sugar

There are many sweeteners for the sort subroutine. Instead of:
sub numerically { if ($a < $b) { return -1; } elsif ($a == $b) { return 0; } else { return 1; } } @nums = (1, 34, 21, 81, -9); @snums = sort numerically @nums;
You can do this:
sub numerically { return $a <=> $b; }
The "<=>" operator returns -1, 0 or 1 depending on whether its left operand is numerically less than, equal, or greater than its right operand. It is affectionately named the "spaceship" operator (after some shoot-em-up text-based game). If you want string comparison use "cmp" instead.

Further, one does not really need to use the 'return' keyword. The last expression that is evaluated is the value that is returned. So we have:

sub numerically { $a <=> $b; }
For the final brevification one can put the body of the sort subroutine between the keyword 'sort' and the array.
my @snums = sort { $a <=> $b } @nums;
Clear as mud? Or as clear as an azure sky of deepest summer?

Caution

Be careful with such compactions. They can quickly become cryptic and unreadable. There is a fine line between brevity and obfuscation.

For an example of how this kind of 'sport' can be taken to an absurd extreme, take a look at Perl Golf!

Exercises

  1. Take the following (somewhat nonsensical) program and apply all of the syntactic sugar that you can. Make it concise, clean and tidy. Test it at each stage of concisification to make sure it functions the same way.
    my @nums = ("zero", "one", "two", "three", "four"); sub by_length { if (length $a < length $b) { return -1; } elsif (length $a > length $b) { return 1; } else { return 0; } } @nums = sort by_length @nums; if (not open(IN, "names")) { print("cannot open names: $!\n"); exit; } my $i = 0; my $total = 0; while (my $line = <IN>) { if (not(length($line) <= 4)) { ; # do nothing } else { $total = $total + length($line); print($nums[length($line)], "\n"); ++$i; if ($i % 10 == 0) { print "."; } } } close(IN); print("\n", 'the total is ', $total, "\n");
  2. Again, make the following more concise:
    sub confirm { if (@_ != 2) { print "confirm needs two parameters\n"; exit; } my ($prompt, $ok) = @_; print $prompt; my $ans = <STDIN>; chomp $ans; return $ans eq $ok; } my $status = confirm("Are you sure? ", "y"); if (not $status) { print "That's all folks!\n"; exit; }

PrevIndexNext