3.2 Writing Code
Your code should be as pleasing to the eye as possible, if for no other reason than you should like looking at it and not recoil at the prospect of again diving into an eructation of characters on the screen. And let's face it, programs need all the aesthetic help they can get because their primary purpose is utilitarian. Which of the following subroutines do you prefer?
sub iterate (&$$){ for ($_[1] = 0,my $i = 0;$i <= $_[2]; $_[1] = ++$i/$_[2]){&{$_[0] }; } }
or
sub iterate (&$$) { for ($_[1] = 0, my $i = 0; $i <= $_[2]; $_[1] = ++$i / $_[2]) { &{$_[0]}; } }
3.2.1 Style
The only believable comment to make about style is that you should have one. If you aren't already rabidly attached to a particular style, read the perlstyle manual page and adopt the one shown.
There's no excuse for not indenting properly. I have seen many people in my classes struggling with an exercise, needlessly handicapped because their code was all hugging the left margin for grim death. Regardless of how quick, short, or temporary your code is, indenting it will save more time than it costs. (The Emacs cperl mode will autoindent simply by hitting the TAB key.)
Use a consistent style for each project.
A clean style is a good style, whatever style you choose. A suggestion: write your code as though you expect it to be published (and win the Turing Award)using white space galore and ample comments.
Pick clear variable names. In general, the less often a variable appears in your code (or the smaller its scope), the longer its name should be since the reader will need more reminding of its purpose. There's nothing wrong though, with single-letter variable names for loop variables (for which they have a long and distinguished history) or mathematical or scientific quantities. ($e=$m*$c**2 makes just as much sense as $energy=$mass* $speed_of_light**2; at least, it does to anyone who should be using code that computes it.)
3.2.2 Help from Your Editor
For those of you concerned about going blind counting and matching (), {}, and [] symbols in your programs, there are tools that can save you from the white cane. A smart editor that can help with some of the routine chores of beautifying a Perl program works wonders. If you have to reindent your code manually every time you remove an outer block, chances are you won't do it. Perl gurus are fond of stating, "Only perl can parse Perl," and as a corollary, no editor can be smart enough to lay out all possible Perl programs correctly. Fair enough; but some of them come close enough to be useful.
One of Peter's favorites is cperl, a Perl mode for Emacs maintained by Ilya Zakharevich. The standard Perl distribution includes an emacs directory that contains the file cperl-mode.el. If your Emacs doesn't already come with cperl, copy this file to wherever you store local Emacs mode files (you can set up a location just for yourself in the .emacs file in your home directory); if you find another version there, determine which cperl-mode.el is more recent. Insert the line
(autoload 'perl-mode "cperl-mode" "alternate mode for editing \ Perl programs" t)
into your .emacs file to enable cperl-mode instead of the regular perl-mode that comes with Emacs.
cperl matches up the various (), [], and {} for you.1 It also performs syntax coloring, so if you configure Emacs to tint all strings in puce and your entire file suddenly blushes, you know you've left a quote mark out somewhere. Most usefully, it tracks nested levels of braces, and a tap of the TAB key takes you to the correct indentation point for the line. This catches all manner of typos, such as the dropped semicolon on line 1:
1 my $marsupial = 'wombat' 2 my $crustacean = 'king crab';
which is exposed when you hit TAB on line 2 and instead of lining up under line 1, the statement indents an extra level because the current statement is incomplete.
cperl does have a few problems, though: if you use the Perl 4 syntax for separating path components in a package qualified identifier with a single quote, it may get confused. But just change the apostrophe (') to colons (::), and you're fine. Using certain characters (like # or ') in character classes in regular expressions confuses it too, but putting backslashes (\) in front of them (backwhacking) resolves the problem.
Other editors we like are: vim/gvim, which does an outstanding job of syntax coloring and has an excellent Windows port; and BBEdit on the Macintosh.
The subject of favorite editors is an intensely religious issue in software development, and you should expect others to disagree with your preference; as long as there are some equally heavyweight developers on your side, it doesn't matter what you pick or what anyone else says.
What should you expect from a good Perl editor? It should warn you when you have mismatched delimiters, it should automatically indent according to the style you want, and it should be as flexible and adaptable to your personal text-editing needs as possible. It should neither insert characters (e.g., invisible binary formatting hints) nor remove any (e.g., trailing spaces) on its own without warning you and giving you the option to disable this functionality selectively or globally.
3.2.3 Think Double
Whether your editor warns you of mismatched delimiters or not, here's a handy tip: enter the closing delimiter at the same time you type the opening one. Then insert what you need between them. Work from the outside in, and your chances of having a grouping typo are greatly reduced. For instance, if you plan to write something that'll end up as:
print table(map { Tr(map td($_), split /[:\s]+/) } <IN>);
(which, using CGI.pm, would print an HTML table from the remaining text coming from the filehandle IN, one row per line, forming table elements from elements separated by white space and/or colons) then your strategy for typing it would follow these steps:
1. print table(); 2. print table(map {} <>); 3. print table(map { Tr() } <IN>); 4. print table(map { Tr(map td(), split //) } <IN>); 5. print table(map { Tr(map td($_), split /[:\s]+/) } <IN>);
3.2.4 Clarity
Examine this very simple one-line program:
$x=1; $y=2; $z=$x+$y; print"$x+$y=$z\n";
This isn't difficult to understand: initialize $x and $y, set $z to their sum, then print all three. Suppose we trace its execution through the debugger (see Chapter 7 for a detailed discussion of the debugger).
% perl -de '$x=1; $y=2; $z=$x+$y; print"$x+$y=$z\n"' main::(-e:1): $x=1; $y=2; $z=$x+$y; print"$x+$y=$z\n"; DB<1> n main::(-e:1): $x=1; $y=2; $z=$x+$y; print"$x+$y=$z\n" DB<1> n main::(-e:1): $x=1; $y=2; $z=$x+$y; print"$x+$y=$z\n" DB<1> n main::(-e:1): $x=1; $y=2; $z=$x+$y; print"$x+$y=$z\n" DB<1> n 1+2=3 Debugged program terminated... DB<1> q
What did we observe? The debugger prints the one-line program:
main::(-e:1): $x=1; $y=2; $z=$x+$y; print"$x+$y=$z\n"
4 times until the expected program output 1+2=3. Why? The debugger displays the next line of code perl executes, not the next command. The program consists of four executable commands, but they're all on the same line; therefore we see the line each time the debugger executes a commend.
Recast the program to one command per line:
$x = 1; $y = 2; $z = $x+$y; print"$x+$y=$z\n";
Then use the debugger. You can either put the four lines in a file or use a shell like the Bourne shell that allows you to type new lines between single quotes.
% perl -d dbformat.pl main::(dbformat.pl:1): $x=1; DB<1> n main::(dbformat.pl:2): $y=2; DB<1> n main::(dbformat.pl:3): $z=$x+$y; DB<1> n main::(dbformat.pl:4): print"$x+$y=$z\n"; DB<1> n 1+2=3 Debugged program terminated... DB<1> q
This is easier to follow. The debugger functioned as it was designed to in both cases, but a style difference made it harder to interpret the debugger's output for the one-liner.