Loren on the Art of MATLAB

November 1st, 2006

A Logical Story

Let me set the stage for today's blog. Logical variables have been part of MATLAB from the early days. They are particularly useful in constructs involved flow-of-control, such as if and while statements. But MATLAB, being an array-oriented language, needs to deal with expressions following if and while that might be any size, including empty matrices. But sometimes users get confused when two statements, seemingly the same, behave differently. The understanding lies in the intersection of scalar expansion, empty arrays, and logical operators, both short-circuit and elementwise.

Contents

Logical Operators, Scalar Expansion, and Empty Arrays

Recently on the MATLAB newsgroup, a user wondered why two statements, that seemed the same, didn't give the same answer. Let's take a look.

if 4 | []
    disp('Must be true')
end
Must be true

MATLAB evaluates

            4 | []

as true.

However, swapping the order of the argument to | results in a false.

if [] | 4
    disp('Must be true')
else
    disp('Must be false')
end
Must be false

How can that be?

Information to Help Unravel the Mystery

There are several concepts that might be helpful for understanding this mystery.

  • Empty arrays evaluate to false for the purposes of if and while.
  • The nonscalar | and & operators short-circuit in if and while expressions, but not otherwise.
  • It's best to feed if and while scalar expressions. This means considering using the newer (MATLAB version 6.5) logical operators || and &&. These operators always short-circuit.
  • Use reduction operations explicitly to reduce expressions to scalar. Examples of these operations include all and any.

Back to the Example

So, what's happening with this code?

if 4 | []
    disp('Must be true')
end
% The 4 is nonzero and is unequivocally true, so that's the result you see.
Must be true

Inverting the statement requires evaluation of the [] first, then a scalar expansion occurs with the 4, yielding [] and that is false.

if [] | 4
    disp('Must be true')
else
    disp('Must be false')
end
Must be false

If you were trying to debug this, however, you'd be tempted to evaluate

dbg = 4 | []
dbg =
     []

You would find dbg is empty (because of scalar expansion) and therefore, would be false according to most of the rules above. However, it does not account for the short-circuit nature of | and & in the if and while.

Notes and Recommendations

If you might have empties that you want to check for, check out isempty.

Use the || and && operators in expressions destined for if and while. These expressions behave consistently, meaning they always short-circuit, and are therefore easier to debug. They also force you to reduce your expression to a scalar and it is easier to see what a simple true or false means in the context of the flow-of-control. Do you have any other guidelines? Share them here.


Get the MATLAB code

Published with MATLAB® 7.3

7 Responses to “A Logical Story”

  1. Oliver A. Chapman, PE replied on :

    There must be some performance benefit to justify, “The nonscalar | and & operators short-circuit in if and while expressions, but not otherwise.” Can you give some examples of this benefit?

    Before this week’s column, I would have thought that the purpose of the operation was return a true if either of the arguments existed. Thus, MatLab doesn’t operate as I expect, which is a flaw and should be corrected.

    Because of the flaw, I have to add complexity to the code, something like this:

    if ~isempty([]) | ~isempty(4)
    disp(’Must be true’)
    else
    disp(’Must be false’)
    end

    if ~isempty(4) | ~isempty([])
    disp(’Must be true’)
    else
    disp(’Must be false’)
    end

    Now I get the expected result.

  2. Bjorn Gustavsson replied on :

    How can you have ‘if’ shortcut ‘|’ with something that is false?
    In my (rapidly made up) opinion this is a severe bug. Lets compare:

    if 0 | 4
    disp(’Must be true’)
    else
    disp(’Must be false’)
    end
    Displays:
    Must be true

    As it should, since | is true since 4 is true. No shortcut in this case.

  3. Ed Limbaugh replied on :

    Wouldn’t the defintion of ’short-circuit’ (with respect to ‘if’ or ‘while’) be to stop evaluating arguments to an ‘or’ or ‘and’ operator as soon as the final result of the operation can be definitively determined? Therefore, an ‘or’ operator (|) should continue evaluating arguments until
    1) it comes to the first TRUE argument, or
    2) it has evaluated ALL of the arguments as false.

    Similarly, I would expect an ‘and’ operator (&) to continue evaluating arguments until
    1) it comes to the first FALSE argument, or
    2) it has evaluated ALL of the arguments as true.

    In the first example (’if 4 | []’), the ‘or’ operator short-circuits after evaluating 4 as a true argument. In the second example (’if [] | 4′), why should ‘or’ stop evaluating? The first argument is false, as you’ve explained: “Empty arrays evaluate to false for the purposes of if and while.” The ‘or’ operator should then continue to evaluate as it has not yet determined whether the remaining arguments are true or false.

  4. Loren replied on :

    All-

    I didn’t explain this well, I think. What happens is that

    [] | 4 does a scalar expansion, ends up [] and therefore evaluates to false. So it’s not the short-circuit that causes the answer, but the scalar expansion. I will update the article to indicate this.

    –Loren

  5. Ed Limbaugh replied on :

    If it is a scalar expansion, why does that not take place in the first example, ‘if 4 | []’? Are the two cases treated differently with respect to operator priority? What are the rules for the priority of evaluating the operators in an ‘if’ or ‘while’ statement? Why would they differ in the two examples?

  6. Loren replied on :

    Ed-

    The reason the 2nd one doesn’t need to do scalar expansion is because it is true itself before doing the next evaluation
    ( xxx | [] ) since 4 is nonzero. The short-circuiting happens as soon as truth can be determined.

    This was meant to demonstrate that you are MUCH safer using || and && and reducing your expressions to scalar values yourself. Your code will more likely be readable later and do as you expect. Using the arrays versions, | and &, lead to weirdness, especially when empty arrays might be around.

    –Loren

  7. Ed Limbaugh replied on :

    Ahhhh. Thanks for clarifying! I understand more clearly now.

Leave a Reply

Wrap code fragments inside <pre> tags, like this:

<pre class="code">
a = magic(3);
sum(a)
</pre>

If you have a "<" character in your code, either follow it with a space or replace it with "&lt;" (including the semicolon).


Loren Shure works on design of the MATLAB language at The MathWorks. She writes here about once a week on MATLAB programming and related topics.

  • Jun: I totally can not believe it, Loren. You are really helpful. Thank you so much, MATLAB master!
  • Loren: Wow folks- Always lots of interest when there’s a quickie to try out! I will only make 2 general...
  • Loren: Jun- ismember is your friend here: >> [aa,ind] = ismember(Array2,Arra y1) aa = 1 1 1 1 1 1 1 ind = 1 2 1 4 4 3...
  • Dan: I like the first way better than the second way. Combining the arrays into one and running any is nice, although...
  • James Myatt: How about I = (a == 0 | b == 0); a(I) = []; b(I) = [];
  • Tunc: Hello Loren, love your blog because of such inspiring and challenging comments to such ’small’...
  • Pekka Kumpulainen: Here is my tradeoff. I usually want to keep the original variables as they are most probably...
  • Iain: Followup: Of course, to allow NaNs (counting them as non-zero): mask = (a~=0) & (b~=0); The mask says “a...
  • Matt Fig: I would usually go with something like this: y = a&b; x = a(y); y = b(y); But I was surprised to find...
  • kk: c=all([a;b]) a(c) a(b)

These postings are the author's and don't necessarily represent the opinions of The MathWorks.