Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Beginning Perl Web Development - From Novice To Professional (2006)

.pdf
Скачиваний:
56
Добавлен:
17.08.2013
Размер:
2.9 Mб
Скачать

320 A P P E N D I X P E R L B A S I C S

Other Tests

What other tests can we perform? We can test if a variable is defined (it must contain something other than the undefined value) using defined().

#!/usr/bin/perl -w

# defined.pl

use strict;

my ($a, $b); $b = 10;

if (defined $a) {

print "\ $a has a value.\ n";

}

if (defined $b) {

print "\ $b has a value.\ n";

}

Not surprisingly, the result we get is this:

$ perl defined.pl $b has a value.

$

You can use this to avoid the warnings you get when you try and use a variable that doesn’t have a value. If we’d tried to say if ($a == $b), Perl would have said

Use of uninitialized value in numeric eq (==)

So we have our basic comparisons. Don’t forget that some functions will return a true value if they were successful and false if they were not. You will often want to check whether the return value of an operation (particularly one that relates to the operating system) is true or not.

Logical Operators

We also saw earlier that we can join together several tests into one by the use of the logical operators. Table A-4 provides a summary of those.

Table A-4. Logical Operators

Operator

Description

$x

and $y

True if both $x and $y are true$x && $y

$x

or $y

True if either of $x or $y, or both are true$x || $y

not $x

True if $x is not true! $x

 

 

 

A P P E N D I X P E R L B A S I C S

321

The operators AND, OR, and NOT are usually used instead of &&, ||, and ! mainly due to their readability. The operator NOT means not, after all. Don’t forget there is a difference in precedence between the two—AND, OR, and NOT all have lower precedence than their symbolic representations.

Multiple Choice: if . . . else

Consider these two if statements:

if ($password eq $guess) { print "Pass, friend.\ n";

}

if ($password ne $guess) {

die "Go away, imposter!\ n";

}

We know that if the first test condition is true, then the second one will not be—we’re asking exactly opposite questions: Are these the same? Are they not the same? In which case, it seems wasteful to do two tests. It’d be much nicer to be able to say, “If the strings are the same, do this. Otherwise, do that.” And in fact we can do exactly that, although the keyword is not “otherwise” but else:

if ($password eq $guess) { print "Pass, friend.\ n";

}else {

die "Go away, imposter!\ n";

}

That’s

if ( condition ) { action } else { alternative action }

Like the if statement, those curly braces are required in the else part.

Even More Choices: if . . . elsif . . . else

Some things in life aren’t clear-cut. In some cases, we’ll want to test more than one condition. When looking at several related possibilities, we’ll want to ask questions such as, “Is this true? If this isn’t, then is that true? If that’s not true, how about the other?” Note that this is distinct from asking three independent questions; whether we ask the second depends on whether or not the first was true. In Perl, we could very easily write something like this:

if ( condition1 ) { action1

}else {

if ( condition2 ) { action2

}else {

if ( condition3 ) { action3

} else {

322 A P P E N D I X P E R L B A S I C S

action4

}

}

}

You might agree that this looks pretty messy. To make it nicer, we can combine the else and the next if into a single word, elsif. Here’s what the preceding would look like when rephrased in this way:

if ( condition1) { action1

}elsif ( condition2 ) { action2

}elsif ( condition3 ) { action3

}else { action4

}

Much neater! We don’t have an awful cascade of closing curly braces at the end, and it’s easier to see what we’re testing, and when we’re testing it.

Let’s look at an example. Most of us will not go outside if it’s raining, but we’ll always go out for a walk in the snow. We will not go outside if it’s less than 18 degrees Celsius. Otherwise, we’ll probably go out unless we’ve got too much work to do. Do we want to go for a walk?

#!/usr/bin/perl -w

# walking.pl

use strict;

print "What's the weather like outside? "; chomp(my $weather = <STDIN>);

print "How hot is it, in degrees? "; chomp(my $temperature = <STDIN>);

print "And how many emails left to reply to? "; chomp(my $work = <STDIN>);

if ($weather eq "snowing") {

print "It's snowing, let's go!\ n";

}elsif ($weather eq "raining") {

print "No way, sorry, it's raining so I'm staying in.\ n";

}elsif ($temperature < 18) { print "Too cold for me!\ n";

}elsif ($work > 30) {

print "Sorry - just too busy.\ n";

}else {

print "Well, why not?\ n";

}

A P P E N D I X P E R L B A S I C S

323

Let’s say it’s 201 degrees, we have 27 e-mails to reply to, and it’s cloudy out there:

$ perl walking.pl

What's the weather like outside? cloudy How hot is it, in degrees? 20

And how many e-mails left to reply to? 27 Well, why not?

$

Looks like we can fit a walk in after all.

The point of this rather silly little program is that once it has gathered the information it needs, it runs through a series of tests, each of which could cause it to finish. First, we check to see if it’s snowing:

if ($weather eq "snowing") {

print "It's snowing, let's go!\ n";

If so, then we print our message and—this is the important part—do no more tests. If not, then we move on to the next test:

}elsif ($weather eq "raining") {

print "No way, sorry, it's raining so I'm staying in.\ n";

Again, if this is true, we stop testing; otherwise, we move on. Finally, if none of the tests are true, we get to the else:

}else {

print "Well, why not?\ n";

}

Please remember that this is very different to what would happen if we used four separate if statements. The tests overlap, so it is possible for more than one condition to be true at once. For example, if it was snowing and we had over 30 e-mails to reply to, we’d get two conflicting answers. elsif tests should be read as “Well, how about if . . . ?”

Another example of using an if/elsif/else is the program we saw earlier, guessnum1.pl. The decision we made in that program was implemented with three if statements:

if ($target == $guess) {

print "That's it! You guessed correctly!\ n"; exit;

}

if ($guess > $target) {

print "Your number is more than my number\ n"; exit;

}

if ($guess < $target){

print "Your number is less than my number\ n"; exit;

}

324 A P P E N D I X P E R L B A S I C S

Notice that in each if statement we execute the exit() function since, if the condition is true, there is no reason to check any of the following conditions. Instead of using the exit() function in each of the if blocks, this would be better written with an if/elsif/else as shown in guessnum2.pl:

#!/usr/bin/perl -w

# guessnum2.pl

use strict;

my $target = 12;

print "Guess my number!\ n"; print "Enter your guess: "; my $guess = <STDIN>;

if ($target == $guess) {

print "That's it! You guessed correctly!\ n";

}elsif ($guess > $target) {

print "Your number is more than my number\ n";

}elsif ($guess < $target) {

print "Your number is less than my number\ n";

}

The unless Statement

There’s another way of saying if (not $a). As always in Perl, there’s more than one way to do it. Some people prefer to think, “If this is not true, then { ... },” but other people think “Unless this is true, then { ... }.” Perl caters to both sets of thought patterns, and we could just as easily have written this:

unless ($a) {

print "\ $a is not true\ n";

}

The psychology is different, but the effect is the same. We’ll see later how Perl provides a few alternatives for these control structures to help them more effectively fit the way you think.

Expression Modifiers

When we’re talking in English, it’s quite normal for us to say

If this is not true, then this happens, or

Unless this is true, this happens.

Similarly, it’s also quite natural to reverse the two phrases, saying

This happens if this is not true, or

This happens unless this is true.

A P P E N D I X P E R L B A S I C S

325

In Perl-speak, we can take this if statement:

if ($number == 0) {

die "can't divide by 0";

}

and rewrite it using expression modifiers as follows:

die "can't divide by 0" if $number == 0;

Notice how the syntax here is slightly different; it’s action if condition. There is no need for parentheses around the condition, and there are no curly braces around the action. Indeed, the indentation isn’t part of the syntax, so we could even put the whole statement on one line. Only a single statement will be covered by the condition. This form of the if statement is called an expression modifier.

We can turn unless into an expression modifier too, so, instead of this:

if (not $name) {

die "\ $name has a false value";

}

you may find it more natural to write this:

die "\ $name has a false value" unless $name;

Using Short-Circuited Evaluation

There is yet another way to do something if a condition is true. By using the fact that Perl stops processing a logical operator when it knows the answer, we can create a sort of unless conditional:

$name or die "\ $name has a false value";

How does this work? Well, it’s reliant on the fact that Perl uses short-circuited, or lazy, evaluation to give a logical operator its value. If we have the statement X or Y, then if X is true, it doesn’t matter what Y is, so Perl doesn’t look at it. If X isn’t true, Perl has to look at Y to see whether or not that’s true. So if $name has a true value, then the die() function will not be executed. Instead, it will do nothing and continue on executing the next statement.

This form of conditional is most often used when checking that something we did succeeded or returned a true value. We will see it often when we’re handling files.

To create a positive if conditional this way, use AND instead of OR. For example, to add one to a counter if a test is successful, you may write

$success and $counter++;

If you’ll recall, and statements are reliant on both substatements being true. So, if $success is not true, Perl won’t bother evaluating $counter++ and upping its value by 1. If $success was true, then it would.

326 A P P E N D I X P E R L B A S I C S

Looping Constructs

Now we know how to do everything once. What about if we need to repeat an operation or series of operations? Of course, there are constructs available to do this in Perl, too.

In programming, there are various types of loops. Some loop forever and are called infinite loops, while most, in contrast, are finite loops. We say that a program “gets into” or “enters” a loop, and then “exits” or “falls out” when finished. Infinite loops may not sound very useful, but they certainly can be—particularly because most languages, Perl included, provide you with a way by which you can exit the loop. They will also be useful for situations when you just want the program to continue running until the user stops it manually, the computer powers down, or the heat death of the universe occurs, whichever is sooner.

There’s also a difference between definite loops and indefinite loops. In a definite loop, you know how many times the block will be repeated in advance. An indefinite loop will check a condition in each iteration to determine whether or not it should do another.

There’s also a difference between an indefinite loop that checks before the iteration and one that checks afterward. The latter will always go through at least one iteration, in order to get to the check, whereas the former checks first and so may not go through any iterations at all.

Perl supports ways of expressing all of these types of loops. First, let’s examine the while loop.

The while Loop

Let’s start with the indefinite loops. These check a condition, then do an action, and then go back and check the condition again. The first one is the while loop. As you might be able to work out from the name, this loop keeps doing something while a condition is true. The syntax of while is much like the syntax of if:

while ( condition ) { action }

Once again, those curly braces are required. Here’s a very simple while loop:

#!/usr/bin/perl -w

# while1.pl

use strict;

my $countdown = 5;

while ($countdown > 0) {

print "Counting down: $countdown\ n"; $countdown--;

}

And here’s what it produces:

$ perl while1.pl Counting down: 5 Counting down: 4 Counting down: 3

A P P E N D I X P E R L B A S I C S

327

Counting down: 2 Counting down: 1

$

Let’s see a flow chart for this program. While there’s still a value greater than 0 in the $counter variable, we do these two statements:

print "Counting down: $countdown\ n"; $countdown--;

Perl goes through the loop a first time when $countdown is 5—the condition is met, so a message gets printed, and $countdown gets decreased to 4. Then, as the flowchart implies, back we go to the top of the loop. We test again: $countdown is still more than 0, so off we go

again. Eventually, $countdown is 1, we print our message, $countdown is decreased, and it’s now 0. This time around, the test fails, and we exit the loop.

while (<STDIN>)

Recall that we talked about using <STDIN> to read from standard input (normally the keyboard). This statement reads the next line of standard input, up to and including the newline character:

$line_in = <STDIN>;

We can put this assignment within a while loop that will read from standard input until end of file (in Unix a ^D or Ctrl+D; in Windows a ^Z<Enter>). This loop reads a line at a time into $line_in and then prints the line read in:

while ($line_in = <STDIN>) { print $line_in;

}

This behavior, reading from standard input until end of file, is so common that if <STDIN> is by itself within the while loop parentheses (and only within the while loop parentheses), then the line of standard input is magically assigned to the special variable $_. This loop reads each line into $_, and then the line is printed:

while (<STDIN>) { print $_;

}

This is so common that print() defaults to printing $_:

while (<STDIN>) { print;

}

Let’s look at an example of using this magic variable $_. This program will loop through standard input one line at a time until end of file, and for each line it will print a message followed by the line entered:

#!/usr/bin/perl -w

# while2.pl

328A P P E N D I X P E R L B A S I C S

use strict;

while (<STDIN>) {

print "You entered: "; print;

}

Following is an example of running this program in Unix:

$ perl while2.pl hello

You entered: hello world

You entered: world good

You entered: good bye

You entered: bye

^D

$

The $_ variable is a useful variable—it is the default argument for many different functions. An example is the chomp() function. The statement

chomp $_;

could have been written as

chomp;

Many Perl programmers find it convenient and readable to write a loop like this one:

while ($line = <STDIN>) { chomp $line;

...

}

using the default nature of $_:

while (<STDIN>) { chomp;

...

}

Whether or not you write code to take advantage of the magic nature of $_ is a choice for you to make, but we suggest you practice with it enough to be able to read code that others have written where $_ is used.

A P P E N D I X P E R L B A S I C S

329

Infinite Loops

The important but obvious point is that what we’re testing gets changed inside the loop. If our condition is always going to give a true result, we have ourselves an infinite loop. Let’s just remove the second of those two statements:

#!/usr/bin/perl -w

# while3.pl

use strict;

my $countdown = 5;

while ($countdown > 0) {

print "Counting down: $countdown\ n";

}

$countdown never changes. It’s always going to be 5, and 5 is, we hope, always going to be more than 0. So this program will keep printing its message until you interrupt it by holding down Ctrl+C. Hopefully, you can see why you need to ensure that what you do in your loop affects your condition.

Should we actually want an infinite loop, there’s a fairly standard way to do it. Just put a true value—typically 1—as the condition:

while (1) {

print "Bored yet?\ n";

}

The converse, of course, is to say while (0) in the loop’s declaration, but nothing will ever happen because this condition is tested before any of the commands in the loop are executed. A bit silly, really.

Looping Until

The opposite of if is unless, and the opposite of while is until. It’s exactly the same as while (not condition) { ... }. Using the condition in the program while1.pl shown previously

while ($countdown > 0) {

its logical negation would be

until ($countdown <= 0) {

Therefore, we can write while1.pl as

#!/usr/bin/perl -w

# until.pl

use strict;

my $countdown = 5;