Logical Operators

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

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 in order to return true. In that case, there's no reason to check the right side, so it will not even 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, 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.[23] We'll see other uses for this behavior later.

[23] But do note that in this idiom the default value won't merely replace undef; it would replace any false value equally well. That's fine for most names, but don't forget that zero and the empty string are useful values that are nevertheless false. 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 be left wishing for something that C had and Perl didn't, so he brought over all of C's operators to Perl.[24] That meant bringing over C's most confusing operator: the ternary ?: operator. While it may be confusing, it can also be quite useful.

[24] 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 an 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 one is ignored. That is, if the first expression is true, then the second expression is evaluated, and the third is ignored. If the first expression is false, then 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 average or just a placeholder line of hyphens, if there's no average available:

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

You could always rewrite any use of the ?: operator as an if structure, often much less conveniently and less 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 just three nested ?: operators, and it works quite well, once you get the hang of it.

Of course, you're not obliged to use this operator. Beginners may wish to avoid it. But you'll see it in others' code, sooner or later, 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 we've just seen &&, ||, and ?: all share a peculiar property: depending upon 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. And partial-evaluation operators are automatically control structures.[25]

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

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.

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:

($a < $b) && ($a = $b);

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

[26] But don't forget to consider that it might be a return value, as the last expression in a subroutine.

If $a is really less than $b, the left side is true, so the right side will be evaluated, thereby doing the assignment. But if $a is not less than $b, the left side will be false, and thus the right side would be skipped. So that line of code would do essentially the same thing as this one, which is easier to understand:

if ($a < $b) { $a = $b; }

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

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

If $a is really 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. Once again, 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 even learn to read these lines as if they were written in English. For example: check that $a is less than $b, and if it is, then do the assignment. Check that $a is more than ten, 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 $c to the smaller of two variables:

($a < $b) ? ($a = $c) : ($b = $c);

If $a is smaller, it gets $c. Otherwise, $b 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.[27] These word-operators have the same behaviors as the ones written with punctuation, but the words are much lower on the precedence chart. Since the words don't "stick" so tightly to the nearby parts of the expression, they may need fewer parentheses:

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

$a < $b and $a = $b;  # 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.

Despite the fact that using logical operators as control structures can be confusing, sometimes they're the accepted way to write code. We'll see a common use of the or operator starting in the next chapter.

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.[28]

[28] Using these weird forms more than once per month counts as overuse.

 



Learning Perl
Learning Perl, 5th Edition
ISBN: 0596520107
EAN: 2147483647
Year: 2001
Pages: 205

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net