Developer

Perl conditions: Sorting out 'and' and 'or'

Perl Boolean operators can be cumbersome and confusing. Learn more about how to use them and clear up any misconceptions at the same time.


Beginners may find Perl's so-called Boolean expressions to be more complicated than those in other languages. For instance, is the Perl AND term written &, &&, or and? Perl has all three, and this article explains which is best.

AND and OR basics
Unless you've been living under a rock for a long time, these statements should be clear:
  • I'll go if Peter goes AND Paul goes.
  • I'll go if Peter goes OR Paul goes.

The first statement says that "Peter goes" and "Paul goes" must both be true before I'll go. So AND means that something is overall true (I'll go) only if both of the parts are true.

The second statement says that "I'll go" is overall true if there exists any part that is true. That's OR. Thank you, George Boole, for making us learn this stuff over and over.

But what about this:
  • I'll go if Peter goes OR Paul goes AND Mary goes.

  • Is just Peter going enough for me to go or not? Let's introduce a precedent rule to be sure. Let's say AND is more important (has a higher precedent) than OR. Perl says it is (and does). Then we know to consider the AND part first and the OR part second. Conclusion: With a rule in place, I'll go if Peter goes, or I'll go if both Paul and Mary go. I won't go if no one goes. This is not new to a programmer, but in Perl, here's the rub: Perl has more than one AND/OR pair following the above rules. When you mix them up, especially with other syntax, there can be odd consequences. Your confidence in Perl syntax can be undermined. Let's see what's happening.

    Common syntax problems
    One of the flexible features of Perl is the way the if statement can be varied.
    if ( $a eq $b or $a = 0 ) { $c = $a && $b; }
    $c = $a && $b if ($a eq $b or $a = 0);
    $c = $a && $b if $a eq $b or $a = 0;

    You can see some Perl AND and OR terms above (&& and or). Trouble starts at the second line. New Perl programmers don’t expect an if to end a statement. In Perl, it can: The if is called a "statement modifier." This second line is the same as the first line. It’s written differently as a matter of personal choice. The third line is even less familiar. Parentheses are often optional in Perl. In line three, they're gone. These parentheses were a cue to the reader. Without them, the code is less clear. This sort of brevity is why some refer to Perl as a "write-only" language, meaning it is "read never"—unreadable. Really, that's due to the programmer's chosen style, not the language.

    If you chunk up these three lines into bits of Perl grammar, you can better see what’s going on:
    if ( expression ) block
    simple-statement if expression;
    simple-statement if expression;

    You can see that the if statement never occurs inside an expression. Now break down the simple-statement term a bit:
    assignee assignment-op expression

    From the example, assignee is $c and assignment-op is =. You can also see that the Boolean terms always appear inside an expression, where ifs never occur. It’s not really possible to tangle them up with ifs, no matter how bad line 3 looks. At least that much is clear and helps when reading the code.

    Here are all of the AND and OR terms ("operators") in Perl:
    and, or, xor, not, &, ~, ^, |, &&, ||, !

    Some of these operators are word-like and some are symbol-like, and Perl statements can be written either way:
    open MYFILE, $info or die "open failed";
    open(MYFILE, $info) || die("open failed");

    Confusion with Perl’s Boolean terms (actually "expressions" or "conditions") occurs when the two styles are mixed—which is perfectly legal. Confusion also occurs when parentheses are dropped. Let's first look at the symbolic case.

    Symbolic syntax
    Symbolic syntax comes from the land of mathematics, where weird punctuation and strange Greek letters are the norm. It's no surprise that the symbolic Boolean operators are:
    &, |, ~, ^

    These are the "bitwise" operators: and, or, exclusive-or, and not. There are also "C-style logical operators," and, or, and not:
    &&, ||, !

    Symbols in the first group are mathematical in nature, and you'd only use them where mathematical computations occur. They work like + and * and play little part in decision logic. They're rarely used inside an if. If you don't code close to the bare metal, you'll never need them.

    The C-style group is of more interest. These are the meat and drink of your code. Use them and be happy:
    if ( $a = $b && $c = $d || $e = $f ) { do_it() }
    $x = $y || $z;

    Precedence rules prevent any ambiguity. "Left-to-right short-circuit Boolean evaluation" also works for you. This means that a condition if statement will be scanned as fast as possible to see whether the condition inside is true, even if that means ignoring some parts of the condition. In the above example, if $a = $b && $c = $d, there's no need to examine $e = $f. Just conclude the whole if must be true straightaway.

    For simple purposes, use && and ||. Life is not always simple, though, as you'll see next.

    Problems with lists and functions
    A big trap is Perl's easy treatment of parentheses. Consider these two lines:
    if (index($str,$substr,0) && $flag ) { do_it() }
    @result = ($arg1,$arg2) || @empty

    These lines work the way you'd expect. In line 1, do_it() is called only if both a flag is true and the index function returns something true. In line 2, the list @result is filled with either the @empty list or the list constructed from $arg1 and $arg2. Note that "," is also an operator—it separates function arguments and list elements.

    If the parentheses are dropped, as Perl allows, the code has problems:
    if ( index $str,$substr,0 && $flag ) { do_it() }
    @result = $arg1,$arg2 || @empty

    The comma operator has very low precedence, even below && and ||, and this mucks everything up. These lines are actually equivalent to:
    if ( index($str,$substr,(0 && $flag)) ) { do_it() }
    @result = ($arg1, ($arg2 || @empty))

    Almost certainly, this is unintended nonsense. A further classic example:
    print STDOUT, "string" || die

    Whoops! die was evaluated (and stops the program) before anything was printed. Moral of the story: When using && and ||, never drop parentheses. Not ever.

    Wordy syntax
    Back at the beginning, we said that Perl encourages a wordy style of coding as well as a symbolic style. This wordy style includes the following Boolean operators:
    and, or, xor, not

    Each one is as you see it: and means AND. These operators have really low precedence, even below the comma operator. So the problem lines that occurred when parentheses were dropped can be fixed with these operators:
    if ( index $str,$substr,0 and $flag ) { do_it() }
    @result = $arg1,$arg2 or @empty

    This tactic, however, is bad form. The if, for example, was symbolically written to begin with. Using wordy syntax to plug a hole created by incomplete punctuation is a bit slack. You're better off putting your parentheses in properly, rather than trying to get around the shortcoming.

    A better use of these operators is to enhance a line that is already wordy. For example, this line is very lightly punctuated, so using or is a natural way to make it readable. It also handles the unpunctuated list of arguments correctly:
    print STDOUT, "string" or die

    Summary
    Of the three collections of Boolean operators in Perl, two differ only in stylistic use and in precedence. If your code is laden with punctuation, don't break it up with and and or. Keep the parentheses and call it art. If you have simple code that is lightly punctuated, using and and or can enhance readability greatly—always a goal for Perl. Just don't mix the two styles if you can help it.

    Editor's Picks

    Free Newsletters, In your Inbox