Документация
HTML CSS PHP PERL другое

Section 10.9.  Logical Operators

 
Previous
Table of Contents
Next

10.9. Logical Operators

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)
    }

There may be a shortcut. If the left side of a logical AND operation is false, the whole thing is false since logical AND needs both sides to be true to return true. In that case, there's no reason to check the right side, so it will not be evaluated. Consider what happens in this example if $hour is 3:

    if ( (9 <= $hour) && ($hour < 17) ) {
      print "Aren't you supposed to be at work...?\n";
    }

Similarly, if the left side of a logical OR operation is true, the right side will not be evaluated. Consider what happens here if $name is fred:

    if ( ($name eq 'fred') || ($name eq 'barney') ) {
      print "You're my kind of guy!\n";
    }

Because of this behavior, these operators are called "short-circuit" logical operators. They take a short circuit to the result whenever they can. In fact, it's fairly common to rely upon this short-circuit behavior. Suppose you need to calculate an average:

    if ( ($n != 0) && ($total/$n < 5) ) {
      print "The average is below five.\n";
    }

In that example, the right side will be evaluated only if the left side is true, so we can't accidentally divide by zero and crash the program.

10.9.1. 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 and not just a Boolean value. This provides the same result, in that the last part evaluated is true when the whole thing should be true, and it's false when the whole thing should be false.

But it's a more useful return value. Among other things, the logical OR operator is 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.[*] You'll see other uses for this behavior later.

[*] In this idiom, the default value won't merely replace undef but will replace any false value equally well. That's fine for most names, but zero and the empty string are useful yet false values. This idiom should be used only when you're willing to replace any false value with the expression on the right.

10.9.2. The Ternary Operator, ?:

When Larry was deciding which operators to make available in Perl, he didn't want former C programmers to miss for something that C had and Perl didn't, so he brought over all of C's operators to Perl.[*] That meant bringing over C's most confusing operator: the ternary ?: operator. While it may be confusing, it can also be quite useful.

[*] Well, to be sure, he did leave out the ones that have no use in Perl, such as the operator that turns a number into the memory address of a variable. And he added several operators (like the string concatenation operator), which make C folks jealous of Perl.

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

    expression ? if_true_expr : if_false_expr

First, the expression is evaluated to see whether it's true or false. If it's true, the second expression is used; otherwise, the third expression is used. Every time, one of the two expressions on the right is evaluated, and the other is ignored. That is, if the first expression is true, the second expression is evaluated, and the third is ignored. If the first expression is false, the second is ignored, and the third is evaluated as the value of the whole thing.

In this example, the result of the subroutine &is_weekend determines which string expression will be assigned to the variable:

    my $location = &is_weekend($day) ? "home" : "work";

And here, we calculate and print out an averageor just a placeholder line of hyphens, if there's no average available:

    my $average = $n ? ($total/$n) : "-----";
    print "Average: $average\n";

You could rewrite any use of the ?: operator as an if structure, often less conveniently and concisely:

    my $average;
    if ($n) {
      $average = $total / $n;
    } else {
      $average = "-----";
    }
    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

That is really three nested ?: operators, and it works quite well once you get the hang of it.

You're not obliged to use this operator. Beginners may wish to avoid it. But you'll see it in others' code, and we hope that one day you'll find a good reason to use it in your own programs.

10.9.3. Control Structures Using Partial-Evaluation Operators

These three operators that you've just seen&&, ||, and ?:all share a peculiar property: depending on whether the value on the left side is true or false, 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. 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 operators into Perl, they automatically became control structures as well. After all, anything that can activate and deactivate a chunk of code is a control structure.

[*] Some of you were wondering why these logical operators are being covered in this chapter, weren't you?

Fortunately, you'll notice this only when the controlled expression has side effects, like altering a variable's value or causing some output. For example, suppose you ran across this line of code:

    ($m < $n) && ($m = $n);

Right away, you should notice that the result of the logical AND isn't being assigned anywhere.[Section 10.9.  Logical Operators] Why not?

[Section 10.9.  Logical Operators] Though it might be a return value, as the last expression in a subroutine.

If $m is less than $n, the left side is true and the right side will be evaluated, thereby doing the assignment. But if $m is not less than $n, the left side will be false, and the right side will be skipped. That line of code will do essentially the same thing as this one, which is easier to understand:

    if ($m < $n) { $m = $n }

Maybe you'll be maintaining a program, and you'll see a line like this one:

    ($m > 10) || print "why is it not greater?\n";

If $m is greater than ten, the left side is true, and the logical OR is done. But if it's not, the left side is false, and this will go on to print the message. This could (and probably should) be written in the traditional way, probably with if or unless.

If you have a particularly twisted brain, you might learn to read these lines as if they were written in English. For example, check that $m is less than $n, and if it is, then do the assignment. Check that $m is more than 10, or if it's not, then print the message.

It's generally former C programmers or old-time Perl programmers who most often use these ways of writing control structures. Why do they do it? Some have the mistaken idea that these are more efficient. Some think these tricks make their code cooler. Some are merely copying what they saw someone else do.

In the same way, the ternary operator may be used for control. In this case, we want to assign $x to the smaller of two variables:

    ($m < $n) ? ($m = $x) : ($n = $x);

If $m is smaller, it gets $x. Otherwise, $n does.

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:

[*] There are the low-precedence not (like the logical-negation operator, !) and the rare xor.

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

Then again, you may need more parentheses. Precedence is a bugaboo. Be sure to use parentheses to say what you mean unless you're sure of the precedence. Nevertheless, since the word forms are very low precedence, you can generally understand that they cut the expression into big pieces, doing everything on the left first, and then (if needed) everything on the right.

Though using logical operators as control structures can be confusing, sometimes they're the accepted way to write code. The idiomatic way of opening a file in Perl looks like this:

    open CHAPTER, $filename
      or die "Can't open '$filename': $!";

By using the low-precedence short-circuit or operator, we're telling Perl it should "open this file... or die!" If the open succeeds, returning a true value, the or is complete. But if it fails, the false value causes the or to evaluate the part on the right, which will die with a message.

So, using these operators as control structures is part of idiomatic Perl, Perl as she is spoken. Used properly, they can make your code more powerful; otherwise they can make your code unmaintainable. Don't overuse them.[Section 10.9.  Logical Operators]

[Section 10.9.  Logical Operators] Using these weird forms (anything but or die) more than once per month counts as overuse.

    Previous
    Table of Contents
    Next
    © 2000- NIV