343 lines
18 KiB
HTML
343 lines
18 KiB
HTML
<!--startcut ==============================================-->
|
|
<!-- *** BEGIN HTML header *** -->
|
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
|
|
<HTML><HEAD>
|
|
<title>Learning Perl, part 3 LG #65</title>
|
|
</HEAD>
|
|
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF" VLINK="#0000AF"
|
|
ALINK="#FF0000">
|
|
<!-- *** END HTML header *** -->
|
|
|
|
<CENTER>
|
|
<A HREF="http://www.linuxgazette.com/">
|
|
<H1><IMG ALT="LINUX GAZETTE" SRC="../gx/lglogo.png"
|
|
WIDTH="600" HEIGHT="124" border="0"></H1></A>
|
|
|
|
<!-- *** BEGIN navbar *** -->
|
|
<IMG ALT="" SRC="../gx/navbar/left.jpg" WIDTH="14" HEIGHT="45" BORDER="0" ALIGN="bottom"><A HREF="nielsen.html"><IMG ALT="[ Prev ]" SRC="../gx/navbar/prev.jpg" WIDTH="16" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="index.html"><IMG ALT="[ Table of Contents ]" SRC="../gx/navbar/toc.jpg" WIDTH="220" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><A HREF="../index.html"><IMG ALT="[ Front Page ]" SRC="../gx/navbar/frontpage.jpg" WIDTH="137" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="http://www.linuxgazette.com/cgi-bin/talkback/all.py?site=LG&article=http://www.linuxgazette.com/issue65/okopnik.html"><IMG ALT="[ Talkback ]" SRC="../gx/navbar/talkback.jpg" WIDTH="121" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><A HREF="../faq/index.html"><IMG ALT="[ FAQ ]" SRC="./../gx/navbar/faq.jpg"WIDTH="62" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="padala.html"><IMG ALT="[ Next ]" SRC="../gx/navbar/next.jpg" WIDTH="15" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><IMG ALT="" SRC="../gx/navbar/right.jpg" WIDTH="15" HEIGHT="45" ALIGN="bottom">
|
|
<!-- *** END navbar *** -->
|
|
<P>
|
|
</CENTER>
|
|
|
|
<!--endcut ============================================================-->
|
|
|
|
<H4 ALIGN="center">
|
|
"Linux Gazette...<I>making Linux just a little more fun!</I>"
|
|
</H4>
|
|
|
|
<P> <HR> <P>
|
|
<!--===================================================================-->
|
|
|
|
<center>
|
|
<H1><font color="maroon">Learning Perl, part 3</font></H1>
|
|
<H4>By <a href="mailto:ben-fuzzybear@yahoo.com">Ben Okopnik</a></H4>
|
|
</center>
|
|
<P> <HR> <P>
|
|
|
|
<!-- END header -->
|
|
|
|
|
|
|
|
|
|
<br><tt>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.</tt>
|
|
<br><tt> -- Larry Wall</tt>
|
|
<p><tt>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.</tt>
|
|
<br><tt> -- Mark-Jason Dominus in comp.lang.perl.misc</tt>
|
|
<br>
|
|
<br>
|
|
<p><b>Overview</b>
|
|
<p>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...
|
|
<br>
|
|
<br>
|
|
<p><b>Conditionals</b>
|
|
<p>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.
|
|
<p>
|
|
<HR NOSHADE WIDTH="100%">
|
|
<br><tt>if ( traffic_light_is_red ) {
|
|
# If condition 1 is true, do</tt>
|
|
<br><tt> stop;
|
|
# Action 1</tt>
|
|
<br><tt>}</tt>
|
|
<br><tt>elsif ( traffic_light_is_yellow ) { # If condition 2 is true,
|
|
do</tt>
|
|
<br><tt> hit_the_gas;
|
|
# Action 2</tt>
|
|
<br><tt>}</tt>
|
|
<br><tt>else {</tt>
|
|
<br><tt>
|
|
# In all other cases, do</tt>
|
|
<br><tt> proceed_with_caution;
|
|
# Action 3</tt>
|
|
<br><tt>}</tt>
|
|
<p>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 '<tt>proceed_with_caution</tt>'.
|
|
<p>Unlike C, even single actions must be enclosed in a block (defined by
|
|
the curly brackets):
|
|
<p><tt>if ( $tomato eq "red" ) print "Ripe.\n";
|
|
# WRONG!</tt>
|
|
<br><tt>if ( $tomato eq "red" ) { print "Ripe.\n"; }
|
|
# Right</tt>
|
|
<p>
|
|
<HR NOSHADE WIDTH="100%">
|
|
<p><tt>unless ( $blarg == $foo ) {
|
|
# If condition 1 is false, do</tt>
|
|
<br><tt> print "Unequal!.\n";
|
|
# Action 1</tt>
|
|
<br><tt>}</tt>
|
|
<br><tt>else {
|
|
# Otherwise, do</tt>
|
|
<br><tt> print "They're equal.\n";
|
|
# Action 2</tt>
|
|
<br><tt>}</tt>
|
|
<p>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".
|
|
:)
|
|
<br>
|
|
<br>
|
|
<p><b>Loops</b>
|
|
<p>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
|
|
<br>common types of loops under Perl:
|
|
<p>
|
|
<HR NOSHADE WIDTH="100%">
|
|
<br><tt>while ( $cat eq "away" ) {
|
|
# While cond. 1 is true, do</tt>
|
|
<br><tt> print "The mice will play.\n";
|
|
# Action 1</tt>
|
|
<br><tt>}</tt>
|
|
<p>
|
|
<HR NOSHADE WIDTH="100%">
|
|
<br><tt>until ( $time > 1159 ) {
|
|
# While cond. 1 is false, do</tt>
|
|
<br><tt> print "It's morning.\n"
|
|
# Action 1</tt>
|
|
<br><tt>}</tt>
|
|
<p>
|
|
<HR NOSHADE WIDTH="100%">
|
|
<br>The "for" loop can be implemented in two different ways - one is like
|
|
the "for" loop in C:
|
|
<p><tt>for ( $n = 99; $n > 0; $n-- ) {</tt>
|
|
<br><tt> print "$n bottles of beer on the wall, $n bottles
|
|
of beer,";</tt>
|
|
<br><tt> ...</tt>
|
|
<br><tt>}</tt>
|
|
<p>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.
|
|
<p>The second method, somewhat like the Clipper, FoxPro, etc. "foreach"
|
|
loops, is by far the most common:
|
|
<p><tt>foreach $n ( 0..1000 ) {</tt>
|
|
<br><tt> print "Day $n on this
|
|
deserted island. So far, I've had ";</tt>
|
|
<br><tt> print $n * 100, " bananas.
|
|
I hope I'm rescued soon.\n";</tt>
|
|
<br><tt> ...</tt>
|
|
<br><tt>}</tt>
|
|
<p>It can also be used this way:
|
|
<p><tt>for ( 0..1000 ) {</tt>
|
|
<br><tt> print "Day $_ on this deserted island. So far,
|
|
I've had ";</tt>
|
|
<br><tt> print $_ * 100, " bananas. I hope I'm rescued
|
|
soon.\n";</tt>
|
|
<br><tt> ...</tt>
|
|
<br><tt>}</tt>
|
|
<p>Our old friend, the "$_" (explained in
|
|
<A HREF="../issue64/okopnik.html">the previous part of this series</A>.)
|
|
He does indeed come in handy. Note that "foreach" is just an alias for
|
|
"for", and they can be used interchangeably.
|
|
<p>
|
|
<HR NOSHADE WIDTH="100%">
|
|
<p>All of the above conditionals and loops can also be used as single-statement
|
|
modifiers, as well:
|
|
<p><tt>print "This is line $_ of 50.\n" for ( 1..50 );</tt>
|
|
<p>The above will print 50 lines, numbered in an obvious way.
|
|
<p><tt>print "I've found him!" if /Waldo/;</tt>
|
|
<p>The above line will be printed if the default buffer ($_) contains a
|
|
match for "Waldo".
|
|
<br>
|
|
<p>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:
|
|
<p><tt>print if $_;
|
|
# Prints $_ if it contains anything</tt>
|
|
|
|
<P> The next example shows that a zero value is also false:
|
|
<PRE>
|
|
print "5280 is true.\n" if 5280; # This will print.
|
|
print "0 is true.\n" if 0; # This won't print.
|
|
</PRE>
|
|
|
|
<P> Here's an example with a list:
|
|
|
|
<p><tt>while ( @a ) {</tt>
|
|
<br><tt> print pop @a;
|
|
# "Pop" the last value off @a and print it</tt>
|
|
<br><tt> $count = @a;
|
|
# Get the number of elements in @a</tt>
|
|
<br><tt> print $count, " elements left in
|
|
\@a.\n";</tt>
|
|
<br><tt>}</tt>
|
|
<p>When the last element has been popped off, the loop will end.
|
|
<p><tt>unless ( %hash ) {</tt>
|
|
<br><tt> %hash = ( 'first' =>
|
|
'Mighty Joe',</tt>
|
|
<br><tt>
|
|
'last' => 'Young',</tt>
|
|
<br><tt>
|
|
'type' => 'gorilla',</tt>
|
|
<br><tt>
|
|
'from' => 'Pangani Mountains',</tt>
|
|
<br><tt>
|
|
'born' => '1949',</tt>
|
|
<br><tt>
|
|
'Mom' => 'Jill',</tt>
|
|
<br><tt>
|
|
'Dad' => 'Gregg'</tt>
|
|
<br><tt> );</tt>
|
|
<br><tt>}</tt>
|
|
<p>If "%hash" is empty, we populate it with some initial values.
|
|
<br>
|
|
<p>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:
|
|
<p><tt>foreach $letter ( reverse 'a'..'z' ) {</tt>
|
|
<br><tt> print "$letter\n";</tt>
|
|
<br><tt>}</tt>
|
|
<p>It will also properly increment "letter lists":
|
|
<p><tt>for ( 'aa'..'zz' ) {</tt>
|
|
<br><tt> print "$_ ";
|
|
# Will print "aa ab ac ... zx zy zz"</tt>
|
|
<br><tt>}</tt>
|
|
<br>
|
|
<br>
|
|
<p><b>User Input</b>
|
|
<p>Capturing keyboard input, or input from STDIN in general - such as the
|
|
lines piped to the input of our script via something like
|
|
<p><tt>cat file | perl_script</tt>
|
|
<p> - is easy; it's what Perl's "diamond operator" is for.
|
|
<br>
|
|
<p><tt>while ( <> ) { # Capture
|
|
all keyboard or piped input</tt>
|
|
<br><tt> print;
|
|
# Print each line as long as input exists</tt>
|
|
<br><tt>}</tt>
|
|
<p>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:
|
|
<p><tt>print while <>;</tt>
|
|
<p>for a more "Perlish" syntax. Note that "<>" and "<STDIN>" are
|
|
related but not equivalent:
|
|
<p><tt>print while <STDIN>;</tt>
|
|
<p>will respond to keyboard and piped input, but <b>will not</b> 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 "<>".
|
|
<p>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:
|
|
<p><tt>$answer = <>; # Get
|
|
the input, assign it to the variable</tt>
|
|
<br><tt>if ( $answer eq "y" ) {</tt>
|
|
<br><tt> print "Yes\n";</tt>
|
|
<br><tt>}</tt>
|
|
<br><tt>elsif ( $answer eq "n" ) {</tt>
|
|
<br><tt> print "No\n";</tt>
|
|
<br><tt>}</tt>
|
|
<br><tt>else {</tt>
|
|
<br><tt> print "No idea!\n";</tt>
|
|
<br><tt>}</tt>
|
|
<p>The above script will always print "No idea!" Hmm... it <i>looks</i>
|
|
right; what could be the problem?
|
|
<p>The problem is that Perl captures <b>everything</b> 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
|
|
<i>and</i> the linefeed. How do we deal with that? Perl, of course, has
|
|
a function - one you should <b>always</b> use when getting user input:
|
|
<p><tt>chomp ( $answer = <> );</tt>
|
|
<p>"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").
|
|
<br>
|
|
<br>
|
|
<p><b>Exercises For The Mind</b>
|
|
<p>Try building a couple of scripts, just for your own education and entertainment:
|
|
<p>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.)
|
|
<p>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>
|
|
<p>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>
|
|
<p>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!
|
|
<br>
|
|
<p><tt>#!/usr/bin/perl -w</tt>
|
|
<br><tt>print "See you next month!"</tt>
|
|
<br>
|
|
<p>Ben Okopnik
|
|
<br><tt>perl -we'print reverse split//,"rekcah lreP rehtona tsuJ"'</tt>
|
|
<br>
|
|
<HR NOSHADE WIDTH="100%"><tt>References:</tt><tt></tt>
|
|
<p><tt>Relevant Perl man pages (available on any pro-Perl-y configured</tt>
|
|
<br><tt>system):</tt><tt></tt>
|
|
<p><tt>perl - overview
|
|
perlfaq - Perl FAQ</tt>
|
|
<br><tt>perltoc - doc TOC
|
|
perldata - data structures</tt>
|
|
<br><tt>perlsyn - syntax
|
|
perlop - operators/precedence</tt>
|
|
<br><tt>perlrun - execution
|
|
perlfunc - builtin functions</tt>
|
|
<br><tt>perltrap - traps for the unwary perlstyle - style guide</tt><tt></tt>
|
|
<p><tt>"perldoc", "perldoc -q" and "perldoc -f"</tt>
|
|
|
|
|
|
|
|
|
|
<!-- *** BEGIN copyright *** -->
|
|
<P> <hr> <!-- P -->
|
|
<H5 ALIGN=center>
|
|
|
|
Copyright © 2001, Ben Okopnik.<BR>
|
|
Copying license <A HREF="../copying.html">http://www.linuxgazette.com/copying.html</A><BR>
|
|
Published in Issue 65 of <i>Linux Gazette</i>, April 2001</H5>
|
|
<!-- *** END copyright *** -->
|
|
|
|
<!--startcut ==========================================================-->
|
|
<HR><P>
|
|
<CENTER>
|
|
<!-- *** BEGIN navbar *** -->
|
|
<IMG ALT="" SRC="../gx/navbar/left.jpg" WIDTH="14" HEIGHT="45" BORDER="0" ALIGN="bottom"><A HREF="nielsen.html"><IMG ALT="[ Prev ]" SRC="../gx/navbar/prev.jpg" WIDTH="16" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="index.html"><IMG ALT="[ Table of Contents ]" SRC="../gx/navbar/toc.jpg" WIDTH="220" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><A HREF="../index.html"><IMG ALT="[ Front Page ]" SRC="../gx/navbar/frontpage.jpg" WIDTH="137" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="http://www.linuxgazette.com/cgi-bin/talkback/all.py?site=LG&article=http://www.linuxgazette.com/issue65/okopnik.html"><IMG ALT="[ Talkback ]" SRC="../gx/navbar/talkback.jpg" WIDTH="121" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><A HREF="../faq/index.html"><IMG ALT="[ FAQ ]" SRC="./../gx/navbar/faq.jpg"WIDTH="62" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="padala.html"><IMG ALT="[ Next ]" SRC="../gx/navbar/next.jpg" WIDTH="15" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><IMG ALT="" SRC="../gx/navbar/right.jpg" WIDTH="15" HEIGHT="45" ALIGN="bottom">
|
|
<!-- *** END navbar *** -->
|
|
</CENTER>
|
|
</BODY></HTML>
|
|
<!--endcut ============================================================-->
|