The trouble with teaching Perl as a first computer language is
that your students won't appreciate it till they start learning their second.
The trouble with teaching Perl as a second language is that there's no
single suitable first language to go in front.
-- Larry Wall
When they say that Perl is a `glue language', what they really mean
is that it is good for cleaning up after the mistakes of other programs.
-- Mark-Jason Dominus in comp.lang.perl.misc
Overview
This month, we'll look at Perl's conditional and looping constructs,
and look at a few scripts that use them. We will also explore how they
work with Perl's variables, and take a quick look at capturing user input.
Once you understand this part, I suggest hacking out a couple of experimental
scripts and playing with them; sure, you'll make mistakes - but from this
point on, you'll actually need to supplement your reading by getting down
and dirty. If you don't play, you can't win...
Conditionals
Here are the conditional statements that Perl uses; nothing particularly unusual, if you're used to conditionals in other languages. Perl checks if the condition is true or false, and branches the execution based on that.
Note that the "elsif" clause isn't required; neither is the "else". Also, note that "else" is the 'catch-all option': if the light is anything except red or yellow - burned out, just got knocked down by accident, etc. - the action is 'proceed_with_caution'.
Unlike C, even single actions must be enclosed in a block (defined by the curly brackets):
if ( $tomato eq "red" ) print "Ripe.\n";
# WRONG!
if ( $tomato eq "red" ) { print "Ripe.\n"; }
# Right
unless ( $blarg == $foo ) {
# If condition 1 is false, do
print "Unequal!.\n";
# Action 1
}
else {
# Otherwise, do
print "They're equal.\n";
# Action 2
}
Pretty obvious. It may help to think of "unless" as the "if not" conditional.
Once again, the "else" is optional. No, there's no such thing as "elseunless".
:)
Loops
Ah, wonderful loops. These are the things that make actions happen,
as many times as we want, based on a condition. You might even say that
this loops are the main reasons for computers in general, their main use
as the tool that they are: precise repetitive work. Here are the three
most
common types of loops under Perl:
for ( $n = 99; $n > 0; $n-- ) {
print "$n bottles of beer on the wall, $n bottles
of beer,";
...
}
In this case, we set $n to an initial value (99), decrement it by 1 each time we go through the loop, and check to make sure that it's greater than 0. If it's not, we exit the loop.
The second method, somewhat like the Clipper, FoxPro, etc. "foreach" loops, is by far the most common:
foreach $n ( 0..1000 ) {
print "Day $n on this
deserted island. So far, I've had ";
print $n * 100, " bananas.
I hope I'm rescued soon.\n";
...
}
It can also be used this way:
for ( 0..1000 ) {
print "Day $_ on this deserted island. So far,
I've had ";
print $_ * 100, " bananas. I hope I'm rescued
soon.\n";
...
}
Our old friend, the "$_" (explained in the previous part of this series.) He does indeed come in handy. Note that "foreach" is just an alias for "for", and they can be used interchangeably.
All of the above conditionals and loops can also be used as single-statement modifiers, as well:
print "This is line $_ of 50.\n" for ( 1..50 );
The above will print 50 lines, numbered in an obvious way.
print "I've found him!" if /Waldo/;
The above line will be printed if the default buffer ($_) contains a
match for "Waldo".
An interesting fact that combines well with loops and conditionals is that empty variables in Perl return a null value - which is "false". This is perfect for checking them out:
print if $_; # Prints $_ if it contains anything
The next example shows that a zero value is also false:
print "5280 is true.\n" if 5280; # This will print. print "0 is true.\n" if 0; # This won't print.
Here's an example with a list:
while ( @a ) {
print pop @a;
# "Pop" the last value off @a and print it
$count = @a;
# Get the number of elements in @a
print $count, " elements left in
\@a.\n";
}
When the last element has been popped off, the loop will end.
unless ( %hash ) {
%hash = ( 'first' =>
'Mighty Joe',
'last' => 'Young',
'type' => 'gorilla',
'from' => 'Pangani Mountains',
'born' => '1949',
'Mom' => 'Jill',
'Dad' => 'Gregg'
);
}
If "%hash" is empty, we populate it with some initial values.
The range operator, which we've used a couple of times so far, is a useful widget: it allows you to specify a range of numbers or letters. Note that the ranges have to be of the same 'kind' - if you specify ('a'..'Z') or ('A'..'z'), the output will not be what you expect. Also, you cannot specify ('z'..'a'); that won't work either. However, there is an easy way to do that:
foreach $letter ( reverse 'a'..'z' ) {
print "$letter\n";
}
It will also properly increment "letter lists":
for ( 'aa'..'zz' ) {
print "$_ ";
# Will print "aa ab ac ... zx zy zz"
}
User Input
Capturing keyboard input, or input from STDIN in general - such as the lines piped to the input of our script via something like
cat file | perl_script
- is easy; it's what Perl's "diamond operator" is for.
while ( <> ) { # Capture
all keyboard or piped input
print;
# Print each line as long as input exists
}
The above works exactly like "cat" - it will print all input piped to it, will "cat" a file if it's run with the filename used as an argument, and will accept (and echo) user input until you hit Ctrl-D or Ctrl-C. It can also be written this way:
print while <>;
for a more "Perlish" syntax. Note that "<>" and "<STDIN>" are related but not equivalent:
print while <STDIN>;
will respond to keyboard and piped input, but will not print the contents of a file supplied as an argument. I've never found a situation where I needed that kind of functionality, so I simply use "<>".
If you want to assign user input to a variable, Perl also makes that easy - but there's a bit of a trap built in of which you need to be aware:
$answer = <>; # Get
the input, assign it to the variable
if ( $answer eq "y" ) {
print "Yes\n";
}
elsif ( $answer eq "n" ) {
print "No\n";
}
else {
print "No idea!\n";
}
The above script will always print "No idea!" Hmm... it looks right; what could be the problem?
The problem is that Perl captures everything that you give it. So, when you type "y", what's the next key you hit? "Enter", that's what! So, the variable stored in $answer is NOT "y", it's "y\n" - the answer and the linefeed. How do we deal with that? Perl, of course, has a function - one you should always use when getting user input:
chomp ( $answer = <> );
"chomp" will remove the linefeed, or "end-of-line" character, from the
string to which it is applied. It will also remove EOLs from every element
of an array which it receives as an argument. The old Perl4 version, "chop",
removed the last character from a scalar (or from the elements of the array)
no matter what it was; it's still available if you should need it for that
purpose, but for taking user input, use "chomp" (also known, via Perl's
error messages, as the "safe chop").
Exercises For The Mind
Try building a couple of scripts, just for your own education and entertainment:
A script that takes a number as input, and prints "Hello!" that many times. As a bonus, check the input for illegal (non-numeric) characters (hint: use //, the match operator.)
A script that takes the current hour (0-23) as input and says "Good morning", "Dobriy den'", "Guten Abend", or "Buenas noches" as a result. <grin>
If you come up with something particularly clever, don't hesitate to send it to me for the next part of this series: you'll get the credit for writing it, I'll happily dissect it for you, and we'll both become micro-famous and retire to Belize on the proceeds. <laugh>
Don't forget: your shebang line should always contain "-w". If you don't
ask Perl to help you with your mistakes, you'll be wasting a lot of time.
Let the computer do the hard work!
#!/usr/bin/perl -w
print "See you next month!"
Ben Okopnik
perl -we'print reverse split//,"rekcah lreP rehtona tsuJ"'
Relevant Perl man pages (available on any pro-Perl-y configured
system):
perl - overview
perlfaq - Perl FAQ
perltoc - doc TOC
perldata - data structures
perlsyn - syntax
perlop - operators/precedence
perlrun - execution
perlfunc - builtin functions
perltrap - traps for the unwary perlstyle - style guide
"perldoc", "perldoc -q" and "perldoc -f"