More Control Structures

The unless Control Structure

In an if control structure, the block of code is executed only when the conditional expression is true. If you want to execute a block of code only when the conditional is false, change if to unless:

unless ($fred =~ /^[A-Z_]\w*$/i) { 
    print "The value of \$fred doesn't look like a Perl identifier name.\n"; 
}

Using unless says to run the block of code unless this condition is true. It’s just like using an if test with the opposite condition.

The else Clause with unless

You could even have an else clause with an unless. While this syntax is supported, it’s potentially confusing:

unless ($mon =~ /^Feb/) { 
    print "This month has at least thirty days.\n"; 
} else { 
    print "Do you see what's going on here?\n"; 
}

Some people may wish to use this, especially when the first clause is very short (perhaps only one line) and the second is several lines of code. But we’d make this one a negated if, or maybe simply swap the clauses to make a normal if.

The until Control Structure

Sometimes you’ll want to reverse the condition of a while loop. To do that, just use until:

until ($j > $i) { 
    $j *= 2; 
}

This loop runs until the conditional expression returns true. But it’s really just a while loop in disguise, except that this one repeats as long as the conditional is false, rather than true. The conditional expression is evaluated before the first iteration, so this is still a zero-or-more-times loop, just like the while loop. As with if and unless, you could rewrite any until loop to become a while loop by negating the condition. But generally, you’ll find it simple and natural to use until from time to time.

Expression Modifiers

In order to have a more compact notation, an expression may be followed by a modifier that controls it. For example, the if modifier works in a way analogous to an if block:

print "$n is a negative number.\n" if $n < 0;

That gives exactly the same result as if we had used this code, except that we saved some typing by leaving out the parentheses and curly braces:

if ($n < 0) { 
    print "$n is a negative number.\n"; 
}

As we’ve said, Perl folks generally like to avoid typing. And the shorter form reads in English: print this message if $n is less than zero.

Notice that the conditional expression is still evaluated first, even though it’s written at the end. This is backward from the usual left-to-right ordering; in understanding Perl code, you’ll have to do as Perl’s internal compiler does, and read to the end of the statement before you can tell what it’s really doing.

There are other modifiers as well:

&error("Invalid input") unless &valid($input); 
$i *= 2 until $i > $j; 
print " ", ($n += 2) while $n < 10; 
&greet($_) foreach @person;

The expression in parentheses inside the print argument list is noteworthy because it adds two to $n, storing the result back into$n. Then it returns that new value, which will be printed.

These shorter forms read almost like a natural language: call the &greet subroutine for each @person in the list. Double $i until it’s larger than $j. One of the common uses of these modifiers is in a statement like this one:

print "fred is '$fred', barney is '$barney'\n" if $I_am_curious;

Although you can rewrite any of these expressions with modifiers as a block (the “old- fashioned” way), the converse isn’t necessarily true. Only a single expression is allowed on either side of the modifier. So you can’t write something if something while some thing until something unless something foreach something, which would just be too confusing. And you can’t put multiple statements on the left of the modifier. If you need more than just a simple expression on each side, just write the code the old- fashioned way, with the parentheses and curly braces.

With the foreach modifier, there’s no way to choose a different control variable—it’s always $_. Usually, that’s no problem, but if you want to use a different variable, you’ll need to rewrite it as a traditional foreach loop.

The Naked Block Control Structure

The so-called naked block is one without a keyword or condition. That is, suppose you start with a while loop, which looks something like this:

while (condition) { 
    body; 
    body; 
    body; 
}

Now, take away the while keyword and the conditional expression, and you’ll have a naked block:

{ 
    body; 
    body; 
    body; 
}

The naked block is like a while or foreach loop, except that it doesn’t loop; it just executes the body of the loop once, and it’s done. It’s an un-loop!

You’ll see in a while that there are other uses for the naked block, but one of its features is that it provides a scope for temporary lexical variables:

{ 
    print "Please enter a number: "; 
    chomp(my $n = <STDIN>); 
    my $root = sqrt $n; # calculate the square root 
    print "The square root of $n is $root.\n"; 
}

In this block, $n and $root are temporary variables scoped to the block. As a general guideline, all variables should be declared in the smallest scope available. If you need a variable for just a few lines of code, you can put those lines into a naked block and declare the variable inside that block. Of course, if you would need the value of either $n or $root later, you would need to declare them in a larger scope.

The elsif Clause

Every so often, you may need to check a number of conditional expressions, one after another, to see which one of them is true. This can be done with the if control structure’s elsif clause, as in this example:

if ( ! defined $dino) { 
    print "The value is undef.\n"; 
} elsif ($dino =~ /^-?\d+\.?$/) { 
    print "The value is an integer.\n"; 
} elsif ($dino =~ /^-?\d*\.\d+$/) { 
    print "The value is a _simple_ floating-point number.\n"; 
} elsif ($dino eq '') { 
    print "The value is the empty string.\n"; 
} else { 
    print "The value is the string '$dino'.\n"; 
}

Autoincrement and Autodecrement

The autoincrement operator (++) adds one to a scalar variable, like the same operator in C and similar languages:

my $bedrock = 42; 
$bedrock++; # add one to $bedrock; it's now 43

Just like other ways of adding one to a variable, the scalar will be created if necessary:

my @people = qw{ fred barney fred wilma dino barney fred pebbles }; 
my %count; # new empty hash 
$count{$_}++ foreach @people; # creates new keys and values as needed

Similarly, the autodecrement operator (--) subtracts one from a scalar variable:

$bedrock--; # subtract one from $bedrock; it's 42 again
The Value of Autoincrement

You can fetch the value of a variable and change that value at the same time. Put the ++ operator in front of the variable name to increment the variable first and then fetch its value. This is a preincrement:

my $m = 5; 
my $n = ++$m; # increment $m to 6, and put that value into $n

Or put the -- operator in front to decrement the variable first and then fetch its value. This is a predecrement:

my $c = --$m; # decrement $m to 5, and put that value into $c

Here’s the tricky part. Put the variable name first to fetch the value first, and then do the increment or decrement. This is called a postincrement or postdecrement:

my $d = $m++; # $d gets the old value (5), then increment $m to 6 
my $e = $m--; # $e gets the old value (6), then decrement $m to 5

A common use of these operators is in connection with a hash, to identify when an item has been seen before:

my @people = qw{ fred barney bamm-bamm wilma dino barney betty pebbles }; 
my %seen; 
foreach (@people) { 
    print "I've seen you somewhere before, $_!\n" 
        if $seen{$_}++; 
}

The for Control Structure

Perl’s for control structure is like the common for control structure you may have seen in other languages, such as C. It looks like this:

for (initialization; test; increment) { 
    body; 
    body; 
}

To Perl, though, this kind of loop is really a while loop in disguise, something like this:

initialization; 
while (test) { 
    body; 
    body; 
    increment; 
}

The most common use of the for loop, by far, is for making computed iterations:

for ($i = 1; $i <= 10; $i++) { # count from 1 to 10 
    print "I can count to $i!\n"; 
}
The Secret Connection Between foreach and for

It turns out that, inside the Perl parser, the keyword foreach is exactly equivalent to the keyword for. That is, any time Perl sees one of them, it’s the same as if you had typed the other. Perl can tell which you meant by looking inside the parentheses. If you’ve got the two semicolons, it’s a computed for loop (like we’ve just been talking about). If you don’t have the semicolons, it’s really a foreach loop:

for (1..10) { # Really a foreach loop from 1 to 10 
    print "I can count to $_!\n"; 
}

That’s really a foreach loop, but it’s written for. Excepting only beginners’ code, it’s always written for, and you’ll have to do as Perl does and look for the semicolons to tell which kind of loop it is.

Loop Controls

The last Operator

The last operator immediately ends execution of the loop. (If you’ve used the “break” operator in C or a similar language, it’s like that.) It’s the “emergency exit” for loop blocks. When you hit last, the loop is done. For example:

# Print all input lines mentioning fred, until the __END__ marker 
while (<STDIN>) { 
    if (/__END__/) { 
        # No more input on or after this marker line 
        last; 
    } elsif (/fred/) { 
        print; 
    } 
} 
## last comes here #

There are five kinds of loop blocks in Perl. These are the blocks of for, foreach, while, until, or the naked block. The curly braces of an if block or subroutine don’t qualify. As you may have noticed in the example above, the last operator applied to the entire loop block.

The next Operator

Sometimes you’re not ready for the loop to finish, but you’re done with the current iteration. That’s what the next operator is good for. It jumps to the inside of the bottom of the current loop block. After next, control continues with the next iteration of the loop (much like the “continue” operator in C or a similar language):

# Analyze words in the input file or files 
while (<>) { 
    foreach (split) { # break $_ into words, assign each to $_ in turn 
        $total++;
        next if /\W/; # strange words skip the remainder of the loop 
        $valid++; 
        $count{$_}++; # count each separate word ## next comes here ## 
    } 
}

print "total things = $total, valid words = $valid\n"; 
foreach $word (sort keys %count) { 
    print "$word was seen $count{$word} times.\n"; 
}

This one is a little more complex than most of our examples up to this point, so let’s take it step by step. The while loop is reading lines of input from the diamond operator, one after another, into $_; you’ve seen that before. Each time through that loop, another line of input will be in $_.

Inside that loop, the foreach loop is iterating over the return value split. Do you remember the default for split with no arguments? That splits $_ on whitespace, in effect breaking $_ into a list of words. Since the foreach loop doesn’t mention some other control variable, the control variable will be $_. So, we’ll see one word after another in $_.

But didn’t we just say that $_ holds one line of input after another? Well, in the outer loop, that’s what it is. But inside the foreach loop, it holds one word after another. It’s no problem for Perl to reuse $_ for a new purpose; this happens all the time.

The redo Operator

The third member of the loop control triad is redo. It says to go back to the top of the current loop block, without testing any conditional expression or advancing to the next iteration. (If you’ve used C or a similar language, you’ve never seen this one before. Those languages don’t have this kind of operator.) Here’s an example:

# Typing test 
my @words = qw{ fred barney pebbles dino wilma betty }; 
my $errors = 0; 
foreach (@words) { ## redo comes here ## 
    print "Type the word '$_': "; 
    chomp(my $try = <STDIN>); 
    if ($try ne $_) { 
        print "Sorry - That's not right.\n\n"; 
        $errors++; 
        redo; # jump back up to the top of the loop 
    } 
} 
print "You've completed the test, with $errors errors.\n";

Like the other two operators, redo will work with any of the five kinds of loop blocks, and it will work with the innermost loop block when they’re nested.

The big difference between next and redo is that next will advance to the next iteration, but redo will redo the current iteration. Here’s an example program that you can play with to get a feel for how these three operators work:

foreach (1..10) { 
    print "Iteration number $_.\n\n"; 
    print "Please choose: last, next, redo, or none of the above? "; 
    chomp(my $choice = <STDIN>); 
    print "\n"; 
    last if $choice =~ /last/i; 
    next if $choice =~ /next/i; 
    redo if $choice =~ /redo/i; 
    print "That wasn't any of the choices... onward!\n\n"; 
} 
print "That's all, folks!\n";
Labeled Blocks

When you need to work with a loop block that’s not the innermost one, use a label. Labels in Perl are like other identifiers—made of letters, digits, and underscores, but they can’t start with a digit—however, since they have no prefix character, labels could be confused with the names of built-in function names, or even with your own sub- routines’ names. So, it would be a poor choice to make a label called print or if. Because of that, Larry recommends that they be all uppercase.

To label a loop block, just put the label and a colon in front of the loop. Then, inside the loop, you may use the label after last, next, or redo as needed:

LINE: while (<>) { 
    foreach (split) {
        last LINE if /__END__/; # bail out of the LINE loop ... 
    }
}

For readability, it’s generally nice to put the label at the left margin, even if the current code is at a higher indentation. Notice that the label names the entire block; it’s not marking a target point in the code.

It often makes sense to choose a noun as the name of the loop. That is, the outer loop is processing a line at a time, so we called it LINE. If we had to name the inner loop, we would have called it WORD, since it processes a word at a time. That makes it convenient to say things like “(move on to the) next WORD”or “redo (the current) LINE”.

The Ternary Operator, ?:

The ternary operator is like an if-then-else test, all rolled into an expression. It is called a “ternary” operator because it takes three operands. It looks like this:

expression ? if_true_expr : if_false_expr

examples:

my $location = &is_weekend($day) ? "home" : "work";
my $average = $n ? ($total/$n) : "-----"; 
print "Average: $average\n";

Here’s a trick you might see used to code up a nice multiway branch:

my $size = 
    ($width < 10) ? "small" : 
    ($width < 20) ? "medium" : 
    ($width < 50) ? "large" : 
                             "extra-large"; # default

Logical Operators

As you might expect, Perl has all of the necessary logical operators needed to work with Boolean (true/false) values. For example, it’s often useful to combine logical tests by using the logical AND operator (&&) and the logical OR operator (||):

if ($dessert{'cake'} && $dessert{'ice cream'}) { 
    # Both are true 
    print "Hooray! Cake and ice cream!\n"; 
} elsif ($dessert{'cake'} || $dessert{'ice cream'}) { 
    # At least one is true 
    print "That's still good...\n"; 
} else { 
    # Neither is true - do nothing (we're sad) 
}
The Value of a Short-Circuit Operator

Unlike what happens in C (and similar languages), the value of a short-circuit logical operator is the last part evaluated, not just a Boolean value. This provides the same result, in that the last part evaluated is always true when the whole thing should be true, and it’s always false when the whole thing should be false.

But it’s a much more useful return value. Among other things, the logical OR operator is quite handy for selecting a default value:

my $last_name = $last_name{$someone} || '(No last name)';

If $someone is not listed in the hash, the left side will be undef, which is false. So, the logical OR will have to look to the right side for the value, making the right side the default. In this idiom, the default value won’t merely replace undef; it would replace any false value equally well. You could fix that with the ternary operator:

my $last_name = defined $last_name{$someone} ? $last_name{$someone} : '(No last name)';

That’s too much work, and you had to say $last_name{$someone} twice. Perl 5.10 added a better way to do this, and it’s the next section.

The defined-or Operator

In the previous section, you saw us use the || operator to give a default value. That ignored the special case; there the defined value was false but perfectly acceptable as a value. You then saw the uglier version using the ternary operator.

Perl 5.10 gets around this sort of bug with the defined-or operator, //, which short-circuits when it finds a defined value, no matter if that value on the lefthand side is true or false. Even if someone’s last name is 0, this version still works:

use 5.010;

my $last_name = $last_name{$someone} // '(No last name)';

You can see this in action by trying several values with // to see which ones pass through to the default value:

use 5.010;
foreach my $try ( 0, undef, '0', 1, 25 ) { 
    print "Trying [$try] ---> ";
    my $value = $try // 'default';
    say "\tgot [$value]";
}

The output shows that you only get the default string when $try is undef:

Sometimes you want to set a value when there isn’t one already. For instance, when you have warnings enabled and try to print an undefined value, you get an annoying error:

use warnings;
my $name; # no value, so undefined!
printf "%s", $name; # Use of uninitialized value in printf ...

Sometimes that error is harmless. You could just ignore it, but if you expect that you might try to print an undefined value, you can use the empty string instead:

use 5.010; use warnings;
my $name; # no value, so undefined! 
printf "%s", $name // '';
Control Structures Using Partial-Evaluation Operators

The four operators that you’ve just seen—&&, ||, //, and ?:—all share a peculiar property: depending upon the value on the left side, they may or may not evaluate an expression. Sometimes the expression is evaluated, and sometimes it isn’t. For that reason, these are sometimes called partial-evaluation operators, since they may not evaluate all of the expressions around them. And partial-evaluation operators are automatically control structures.* It’s not as if Larry felt a burning need to add more control structures to Perl. But once he had decided to put these partial-evaluation op- erators into Perl, they automatically became control structures as well. After all, any- thing that can activate and deactivate a chunk of code is, by that very fact, a control structure.

There is another way to write the logical AND and logical OR operators. You may wish to write them out as words: and and or. These word operators have the same behaviors as the ones written with punctuation, but the words are down at the bottom of the precedence chart. Since the words don’t “stick” so tightly to the nearby parts of the expression, they may need fewer parentheses:

$m < $n and $m = $n; # but better written as the corresponding if

发表评论

邮箱地址不会被公开。 必填项已用*标注