145

Chapter 10:

Help!

Humpty Dumpty sat on a wall,
Humpty Dumpty had a great fall.
All the kings horses and all the king's men
had scrambled eggs for breakfast again.

-Anonymous, variation on an old English nursery rhyme

A book like this one can only take you so far. Sooner or later (probably soon-
er!), you'll want to start writing scripts of your own, rather than just trying
out our examples. Inevitably, something will go wrong (we hope it's some-
thing small) and you'll need to know where to turn to get help, answers to
your questions, and more information. This chapter gives advice on what to
do and where to look if anything should go worng [sic :-].

Start With A Good Editor

Before you go too far, we recommend that you start using a good text editor.
Although MacPerl comes with an editor of its own, you'll probably exceed
its range of capabilities fairly soon. The MacPerl internal editor is based on
TextEdit, the same engine Apple used first in
TeachTextand later in Sim-
pleText
. A simple (perhaps too simple) editor for creating README files
(its original purpose) and small MacPerl scripts, it has several limitations.

The MacPerl text editor cannot open, or edit, files with more than 32 KB of
text (typically less than a thousand lines of Perl code). It doesn't show tabs
or character spacing consistently, so indentation is very difficult to get
right. Most critically, it has no special features to support programming.


IMAGE imgs/230.Help01.gif

Fortunately, several text editors are available which have excellent sup-
port for writing code in general and MacPerl in particular. Their features
include language-sensitive text coloration, advanced search-and-replace
functionality, regular expressions, split-screen capability (letting you view
a function and the code which calls it simultaneously), indentation (and
exdentation) as well as entabbing and detabbing, and much, much more.

The authors use BBEdit, from BareBones Software. The Alphaeditor (from
Pete Keleher) is also highly recommended by many MacPerl users. Versions
of both editors are included on the MacPerl CD-ROM. MPW users may sim-
ply want to use the MPW shell editor, but should be sure to upgrade to the
latest version if need be. It has many excellent code editing features, includ-
ing text coloring.

AlphaLiteand Alphaare both shareware; AlphaLitehas fewer features than
Alpha. If you continue to use Alphaor AlphaLite, please respect the shareware
license (and the author's willingness to allow us to distribute his programs)
and pay for it!

BBEdit Liteis a freeware derivative of the current commercial version of
BBEdit. The Lite version has fewer capabilities, but is still a powerful edi-
tor.
BBEditis a commercial product; we have included the demonstration
version on the CD. The demo is fully featured, except that saving documents
is disabled and printed documents are given a "BBEdit Demo" watermark.
Information on purchasing the commercial version of BBEdit is included.

Good Habits Cost No More

One of the best things you can do to keep yourself out of trouble is to develop
a good Perl programming style and practice until it becomes habit. The code
in this book is, in all modesty, a good example to follow. By and large, we
follow the style guidelines set forth by Perl's author and documented in the
Perl style guide (under the topic,
Various, in the Helpmenu).

On occasion, we'll break one of these style rules, usually because of the con-
straints inherent in fitting examples into a 6 by 7 inch printed page! Please
bear with us, understanding why we formatted the code that way. In any
case, we will still try to keep our code as clear and easy to read as possible.

The easier your code is to read, the easier it will be to understand (we hope)
and to maintain. Use whitespace for readability. Use parentheses for clar-


IMAGE imgs/230.Help02.gif

ity. Use comments. Eschew obfuscation. Avoid unnecessary "cuteness". On
the other hand, try not to go overboard on any of these recommendations.

Fable:One day, a programmer picked up a program that he had writ-
ten a while back and found he had no ideahow it worked. He studied
and worked for hours, tracing each statement until at last he understood
the code again. At that point, he realized that the logic was obvious
and thus didn't need comments.

Moral:Will you remember how your code works in 6 months? How
"obvious" do you think it will appear to you?

Check the syntax of your script before attempting to run it. Use the Syntax
Check ...
menu item or the -ccommand line option (in MPW).

Make sure that your code runs with Compiler Warningschecked, or using the
-wswitch. Run your code under the use strictpragma. Do not assume
that code written by other people, even modules in the MacPerl distribu-
tion
, are necessarily "
strictclean", however, or that such code will run
without compiler warnings. Rather than trying to prevent all warnings, be
sure you know what they mean (and whether they pose a real problem).

Always check the return status of system calls such as open(). These calls
return success or failure for a reason. Handle error conditions. Print error
messages to
STDERR; don't mix them with the normal output. A good error
message says what went wrong, and, if appropriate, includes the system
error message (e.g.,
$!) as well.

Check your input data for validity. Check the arguments to your script.
Bulletproof your code. Develop an attitude of cynical paranoia toward the
users of your script (including yourself). To paraphrase one of Henry Spen-
cer's Ten Commandments for C Programmers:

... for surely where thou typest "foo" someone someday shall type
"supercalifragilisticexpialidocious".

Read this book. Read the online documentation. Read other books. Read
the example scripts in the MacPerl distribution, on our CD-ROM, and on the
CPAN site. Find and read scripts from other sources. Learn to distinguish
good scripts from not so good scripts. Rewrite the not so good ones and make
them better. Write your own scripts, then rewrite them when you learn
something new. Practice, practice, practice, but be sure to have fun doing it!


IMAGE imgs/230.Help03.gif

Debugging Techniques

Even with all the care in the world, eventually something will go wrong.
You should plan for this occasion, so that you won't be caught unprepared.

There are two major types of errors that you will encounter: syntax errors
and logic errors. For each, there are various debugging techniques that you
can use to help you figure out what went wrong, fix it, and get on with the
fun of writing and running your program.

What Broke?

First of all, it's important to figure out what broke. For example, consider
the following code fragment

if ($a > 0) {
print "greater\n;
}
elsif ($a < 0) {
print "less than\n";
}
else {
print "equal to\n";
}

When MacPerl syntax-checks this code, it produces 18 lines of diagnostics:

# Bareword found where operator expected, near "print "less"
#(Might be a runaway multi-line "" string starting on line 2)
File 'Untitled #3'; Line 5
# (Do you need to predeclare print?)
# syntax error, near "print "less than"
File 'Untitled #3'; Line 5
# String found where operator expected, near "print ""
...

Wow! All that noise from such a little piece of code? Syntax-checking code
frequently produces cascading errors. A small error produces more and more
error messages as the interpreter tries to recover from the initial problem.

The best debugging technique is to look at the first error message first. Find
the problem, fix it, and run the script again. In this case, the first error was

# Bareword found where operator expected, near "print "less"
#(Might be a runaway multi-line "" string starting on line 2)
File 'Untitled #3'; Line 5


IMAGE imgs/230.Help04.gif

If you're using an editor such as BBEditor Alpha, be sure to turn on the line
number display feature; it will make your programming life much easier. If
you're using the MacPerl editor, select the error message line (in the
Mac-
Perl
window) that includes the file name and line number

File 'Untitled #3'; Line 5

then select Jump to ...from the MacPerl Editmenu.

print "less than\n";

Hmmm. That line looks just fine. What broke?

If you look at the code for a little while and nothing appears to be wrong,
don't spend too much time trying to figure out what happened. The problem
may be earlier in the code and this is only the point where MacPerl figured
out that there wasas problem. Take a look at the preceding code, keeping
in mind the complete text of the error message as you do so.

# Bareword found where operator expected, near "print "less"
#(Might be a runaway multi-line "" string starting on line 2)

Ah yes, line 2. The closing double quote is missing.

print "greater\n;

Insert the quote and run the code again. What do you know? It works!

Traces And Other Debugging Tools

Once you have the syntax right, your script should work perfectly. Well,
maybe not. The errors that arise from code that is syntactically correct, but
still (often subtly) wrong, are called logic errors. The following example
looks at some simple logic errors.

This script reads an input file, printing the data to standard output. Each
input line consists of an ID field and some additional data; as long as the ID
field doesn't change, we just keep reading and printing lines. When the ID
field changes, we print some summary information before proceeding to the
next ID. (At least, that's what we had in mind!)

Given this input data:

X12344
X12345
X12345

diamond
apple
banana


IMAGE imgs/230.Help05.gif

Here's the output we want:

X12344 diamond
X12344 = 1 entry
X12345 apple
X12345 banana
X12345 = 2 entries

The code:

#!perl
open (ID, ":Data:IDs.dat");

$count = 0;
while(<ID>) {
chomp();
($id, $entry) = split('\t');

if ($id = $last_id) {
print;
$count++;
} else {
print "$last_id = $count ",
($count > 1) ? 'entries' : 'entry', "\n";
$last_id = $id;
$count = 0;
}
}

The results:

= 0 entry
= 0 entry
= 0 entry

Oh dear; that's not right.

If you can't figure out what went wrong after a little study, try adding some
trace statements. Traces can be as simple as a few
printstatements, or you
might design a
trace()function for more flexibility.1

Let's add a printstatement to display the interesting variables:

IMAGE imgs/230.Help06.gif

1An example of a trace routine is included on the CD-ROM.


IMAGE imgs/230.Help07.gif

while (<ID>) {
($id, $entry) = split('\t');
print("\$id=$id, \$entry=$entry, ",
"\$last_id=$last_id, \$count=$count\n");

We've placed the variables within angle brackets to make it easier to see
if they have null values, or contain whitespace. Here's the output:

$id=X12344, $entry=diamond, $last_id=, $count=0
= 0 entry
$id=X12345, $entry=apple, $last_id=, $count=0
= 0 entry
$id=X12345, $entry=banana, $last_id=, $count=0
= 0 entry

There's the problem; $last_idis never initialized! We'll add a line to set
$last_idto $idon the first line of input.

...
while (<ID>) {
($id, $entry) = split('\t');
$last_id = $id if ($. == 1);
...

Here's the output

$id=X12344, $entry=diamond, $last_id=, $count=0
X12344diamond
$id=X12345, $entry=apple, $last_id=X12344, $count=1
X12345apple
$id=X12345, $entry=banana, $last_id=X12344, $count=2
X12345banana

Still not right. The count just keeps increasing; we never enter the else
block. Take another look at the code ... Oops; we're using an assignment
instead of a comparison!
2

if ($id = $last_id) {

Since $last_idhasn't been set yet, it defaults to 0. So, $idis assigned to
$last_id; the resulting value, 0, is false, so Perl executes the elseblock!
Let's change that and run the script again.

IMAGE imgs/230.Help06.gif

2A common error.


IMAGE imgs/230.Help09.gif

if ($id == $last_id) {

Now the output is

$id=X12344, $entry=diamond, $last_id=, $count=0
X12344 diamond
$id=X12345, $entry=apple, $last_id=X12344, $count=1
X12345 apple
$id=X12345, $entry=banana, $last_id=X12344, $count=2
X12345 banana

That's still not right. We need to examine the data a little more closely to
realize that we're misusing a numericcomparison operator on stringdata.
3
(The IDs begin with letters.) Let's fix the line (again) and re-run the script.

if ($id eq $last_id) {

Let's try it again

$id=X12344, $entry=diamond, $last_id=, $count=0
X12344 diamond
$id=X12345, $entry=apple, $last_id=X12344, $count=1
X12344 = 1 entry
$id=X12345, $entry=banana, $last_id=X12345, $count=0
X12345 banana

That looks pretty good; let's turn off the traces and see what happens.

X12344 diamond
X12344 = 1 entry
X12345 banana

OK, the numbers add up now. Funny thing, though, there's no summary after
banana. And ... wait a minute! Where's the entry for apple?

Even when the output looks good, it's important to check it against the data
and the expected results. In this case, even though the numbers appear to
add up (i.e., the results are plausible), we still have a bug in the code.
We've forgotten to count the first line in each new set (when
$idis new),
and we're not printing the summary information for the final set of entries.

Here's the final code, all tested, debugged, and working, with the traces
removed:

IMAGE imgs/230.Help06.gif

3Another common error.


IMAGE imgs/230.Help11.gif

#!perl
open (ID, ":Data:chapter10.dat");

$count = 0;
while (<ID>) {
chomp();
($id, $entry) = split('\t');
$last_id = $id if ($. == 1);

if ($id eq $last_id) {
print "$_\n";
$count++;
} else {
print "$last_id = $count ",
($count > 1) ? 'entries' : 'entry', "\n";
$last_id = $id;
print "$_\n";
$count = 1;
}
}
print "$last_id = $count ",
($count > 1) ? 'entries' : 'entry', "\n";

Now, that wasn't so hard, was it?

The important thing to remember when testing and debugging a Perl script
is to take your time and keep calm. If you find yourself thrashing (trying
something new just "because", with no real plan or thought), stop and take a
deep breath. If you can't find the bug by looking at it, add some
print
statements. If you get too much output, make the prints conditional.

Or, call someone else over and ask them to help you look for the bug. Walk
your friend through the script, explaining what it's (supposed to be) doing,
line by line. Typically, without your friend saying a word, you'll suddenly
realize that something you just said does notmatch the code you're seeing.
You'll be back on your way to fixing the bug and finishing your script!

The Perl Debugger

If you prefer not to insert printstatements, you may be pleased to learn
that Perl has a fully-functioned source code debugger (just like the fancy
compiled languages :-). Invoke your script with the
-dswitch, or check the
Perl Debuggeritem in the Scriptmenu, to activate the Perl debugger.


IMAGE imgs/230.Help12.gif

The Perl debugger is actually a special version of the standard Perl "Run
Script" environment.
4When you work with the debugger, you're working
inside an interactive Perl environment that knows all about your code. It
also knows how to step through code one line at a time, set breakpoints,
print stack traces, change variable values dynamically, and much more.

Here are a few of the basic debugger commands, to get you started. For com-
plete details, examples, etc., see the
Debuggingitem under Troubleshooting
in the MacPerl Helpmenu.

Command
h
s
n
r
V Foo
V Foo var
X var
x $var
x @var
t
command
q

What it does
print a help message
single step through the code; enters subroutines
skip over a subroutine call without going into it
return from the current subroutine
display all variables in package
Foo
display only variables *varin package Foo
same as V, assumes current package
show the value of
$var
show the value of @var
toggle trace mode
execute the given command as a Perl statement
quit the debugger

RTFM5

MacPerl comes with extensive online help. Over fifty help files in POD
(Plain Ol' Documentation) format are readily available from the MacPerl
Helpmenu. These files are best viewed with the Shuckapplication; the
Lookupand Findmenu items are very handy for locating desired topics.

We recommend that you spend some time familiarizing yourself with the
online help. The top-level categories include:

  • MacPerl Overview
  • Macintosh-specific features
  • Perl FAQ
  • Table of Contents
  • Macintosh Toolbox Modules
  • Syntax
IMAGE imgs/230.Help06.gif

4Specifically, the -dswitch (or the menu item) tells Perl to insert information about
your program's source code into the parse trees it gives to the interpreter.
5Read The Fine Manual


IMAGE imgs/230.Help14.gif
  • Operators and Precedence
  • Built-in functions
  • Data structures
  • Troubleshooting
  • Regular expressions
  • Predefined variables
  • Advanced Topics
  • Various

Recommended Reading

Although we've tried to make this book self-contained, we couldn't tell you
everything there is to know about Perl in one volume. We've concentrated,
instead, on getting you started using (Mac-)Perl. We've covered a subset of
Perl, enough to provide you with a sound basis for learning how to program
and a solid framework for adding more detailed knowledge.

Parts III and IV delve deeper into MacPerl-specific areas, covering topics
that you might not be able to find elsewhere: Mac Toolbox modules, Data
Storage, Mac OS GUI interfaces, etc. As a result, we've had to leave some
things out to make room for other information we thought you should have.

We don't feel too badly about this, however, because we know that this
material is well covered by other books (particularly O'Reilly's fine set of
Perl books). Take another look at our chart in the Introductionand consider
investing in some of these books. Also, consider getting some of the other
books on Perl and related topics that we list in Chapter 23, Books, Etc.

Periodical Publications

More articles and columns about Perl are being written every day. Randal
Schwartz, co-author of Programming Perl, has regular columns in UNIX
Review
andWeb Techniques.Other periodical publications (e.g., SunEx-
pert
) also feature regular or irregular articles on Perl.

The Perl Journal( www.tpj.com) is entirely devoted to the topic of Perl.
Recent articles have included a restrospective account of the creation of
MacPerl, by Matthias Neeracher, and a tutorial discussion of using Apple
Events, by Chris Nandor.

The Perl Community

The Web

You can find many resources and a lot of helpful information on the World
Wide Web. In particular, the primary Perl site ( www.perl.com) contains
recent news bulletins, pointers to the CPAN and the latest version of Perl,


IMAGE imgs/230.Help15.gif

FAQs, Support, and Bug Reporting areas, a searchable Perl Reference, and
more. You should also visit The Perl Institute ( www.perl.org), a "non-
profit organization dedicated to making Perl more useful for everyone".

Finally, there are a number of Web sites becoming available for MacPerl.
The MacPerl Homepage (Matthias Neeracher) and the MacPerl Pages
(Prime Time Freeware) are available via the MacPerl
Helpmenu. Both
sites contain links to other MacPerl-related pages.

The USENET and the MacPerl Mailing List

The Internet provides plenty of opportunity for you to interact with fellow
Perl (and MacPerl) users. On USENET, the
comp.lang.perl.misc news-
group covers myriad Perl topics, up to several hundred messages a day!

The MacPerl mailing list, specializing in MacPerl-specific discussions, has
much lower traffic. To join the mailing list, send email to

mac-perl-request@iis.ee.ethz.ch

with only the word

subscribe

in the body of the message. (The subject will be ignored.) You should receive
a confirmation message by return email.

Before posting a question to either comp.lang.perl.miscor the MacPerl
mailing list, take some time to research the question and check the online
documentation. Your fellow Perl programmers will thank you (and be more
willing to help :-) if your question is interesting, novel, and topical.

The Annual Perl Conference

The first annual Perl conference, sponsored by O'Reilly & Associates, was
held in August 1997, in San Jose, California. Attended by over one thousand
people, the first conference was a huge success. At this writing, the second
conference is planned for August 1998. We believe that this conference will
become a regular, and highly respected, annual event.

The Perl conference is a great opportunity to meet and interact with other
Perl programmers, at all levels. It's also a fantastic opportunity to meet
and hear from some of the leading lights of the Perl community, such as
Larry Wall himself! For more information on upcoming Perl conferences,
visit the conference web site at
conference.perl.com.

Copyright © 1997-1998 by Prime Time Freeware. All Rights Reserved.