cmv has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks!-

I think I need a knowledge recharge on the 'or' versus '||' question, as I'm finding the attached program's results hard to explain.

To start at the beginning, I have a Tk canvas that may or may not have someting on it. I used this syntax...

my @coords = $can->bbox('all') || ( 0, 0, 5, 5);
...to try and have @coords = (0, 0, 5, 5) if the canvas was empty, otherwise have the coordinates of the surrounding box for all its contents.

This didn't work the way I wanted it to, so I came up with this program, partially for fun, and partially to help me understand things better:

use strict; use warnings; use Data::Dumper; use Tk; sub x { return () }; my @list = x() | (1,2,3); print "List:\n",Dumper(\@list), "\n"; my @list0 = x() || (1,2,3); print "List0:\n",Dumper(\@list0), "\n"; my @list1 = x() or (1,2,3); print "List1:\n",Dumper(\@list1), "\n"; my $t=MainWindow->new(); my $c=$t->Scrolled('Canvas')->pack; $c->createArc(5,5,100,100); $t->update; my @list2 = $c->bbox('all') || (1,2,3); print "List2:\n", Dumper(\@list2), "\n"; my @list3 = $c->bbox('all') or (1,2,3); print "List3:\n", Dumper(\@list3), "\n";
My output looks like this:
Useless use of a constant in void context at /tmp/crap line 10. Useless use of a constant in void context at /tmp/crap line 14. Useless use of a constant in void context at /tmp/crap line 14. Useless use of a constant in void context at /tmp/crap line 24. Useless use of a constant in void context at /tmp/crap line 24. Use of uninitialized value in bitwise or (|) at /tmp/crap line 10. List: $VAR1 = [ 3 ]; List0: $VAR1 = [ 1, 2, 3 ]; List1: $VAR1 = []; List2: $VAR1 = [ [ 51, 3, 102, 55 ] ]; List3: $VAR1 = [ 51, 3, 102, 55 ];
I see that the warnings are trying to tell me something, but it's not clear enough to me what that problem(s) is (are).

LIST: was just for fun, but I don't understand why I got what I got

LIST0: I think I understand this one, but then...
LIST1: ...why doesn't this work as well?

LIST2: I really don't understand how I get a list of a list here, and then...
LIST3: ...I'm not sure I understand why this works!

Many thanks in advance for your guidance.

-Craig

Replies are listed 'Best First'.
Re: 'or' versus '||' - Unexpected Results
by ikegami (Patriarch) on Jun 24, 2008 at 16:12 UTC
    • You have three areas of confusion. First, is the context in which operands are evaluated.

      | is a math operation. It requires two numbers on which to operate. Its operands are evaluated in scalar context.

      || is a conditional operator. It requires its LHS to be true of false. Its LHS operand is evaluated in scalar context. Since it doesn't need to inspect the RHS, the context in which the operator is evaluated is propagated to its RHS operand.

      or is identical to || except in precedence.

    • You also seem to have a problem understanding the different precedence of the || and or operators.

      my @list0 = x() || (1,2,3); means my @list0 = ( x() || (1,2,3) );
      my @list1 = x() or (1,2,3); means ( my @list0 = x() ) or (1,2,3);

    • Finally, it appears that bbox returns something different in scalar context ([51,3,102,55]) than in list context ((51,3,102,55)).

    It is not only functioning as documented, but as intentionally and thoughtfully designed.

      Given the above, here's a step by step breakdown of what Perl does.

      c: When the code is compiled. r: When the code is run. my @list = x() | (1,2,3); c => my @list = scalar(x()) | scalar(1,2,3); c => my @list = scalar(x()) | scalar(2,3); <<void warn>> c => my @list = scalar(x()) | scalar(3); <<void warn (supressed)>> c => my @list = scalar(x()) | 3; r => my @list = undef | 3; r => my @list = 0 | 3; <<uninit warning>> r => my @list = 3; my @list0 = x() || (1,2,3); c => my @list0 = scalar(x()) || (1,2,3); r => my @list0 = undef || (1,2,3); r => my @list0 = (1,2,3); my @list1 = x() or (1,2,3); c => ( my @list1 = x() ) or (1,2,3); c => ( my @list1 = x() ) or (2,3); <<void warn>> c => ( my @list1 = x() ) or 3; <<void warn (supressed)>> c => my @list1 = x(); <<void warn>> r => my @list1 = (); my @list2 = $c->bbox('all') || (1,2,3); c => my @list2 = scalar($c->bbox('all')) || (1,2,3); r => my @list2 = [ ... ] || (1,2,3); r => my @list2 = [ ... ]; my @list3 = $c->bbox('all') or (1,2,3); c => ( my @list3 = $c->bbox('all') ) or (1,2,3); c => ( my @list3 = $c->bbox('all') ) or (2,3); <<void warn>> c => ( my @list3 = $c->bbox('all') ) or 3; <<void warn (supressed)>> c => my @list3 = $c->bbox('all'); <<void warn>> r => my @list3 = ( ... );
      ikegami++ for the quick reply! Update: Thanks for answering some of this while I was typing it in

      My areas of confusion span the far reaches of the known universe, however for this problem, I can live with just 3 :-)

      I'm interested to understand why the '|' math operation decided to return a 3 for me, when given two non-numbers to operate on. This was the fun part.

      Thanks for the precedence pointer - I always seem to forget about that (duh - basic stuff)...

      The context confusion is the key - thanks for untangling my knotted mind. I see that this works:

      my @list2 = @{$c->bbox('all') || (1,2,3)};
      However, this won't work when the canvas is empty. Is there a way to write this that will work for both empty and non-empty canvas?

      Thanks

      -Craig

        I'm interested to understand why the '|' math operation decided to return a 3 for me, when given two non-numbers to operate on. This was the fun part.

        (3,4,5) | (7,8,9) === ((3,4),5) | ((7,8),9) === (4,5) | ((7,8),9) === 5 | ((7,8),9) === 5 | (8,9) === 5 | 9 === 13

        It's a common misconception that (..., ..., ...) is a list. It's simply two instances of the binary operator ",". In list context, "," returns a list consisting of both operands (which are evaluated in list context). In scalar context, "," returns its RHS operand (after evaluating both sides in scalar context).

        Is there a way to write this that will work for both empty and non-empty canvas?

        Close. Since you're dereferencing ("@{}") the result of the "||", you need to return an array reference on both sides of the "||".

        my @list2 = @{ $c->bbox('all') || [ 1,2,3 ] };

        Or if you want to avoid creating an array and a reference to it just so it can be derefenced and listed,

        my @list2 = $c->bbox('all'); @list2 = (1,2,3) if !@list2;
        As I posted below, '||' will not return the (true) left-hand-side in list context; you can use:
        my @t; my @list = (@t = $c->bbox()) ? @t : (1, 2, 3);
        and it'll work.
Re: 'or' versus '||' - Unexpected Results
by swampyankee (Parson) on Jun 24, 2008 at 16:28 UTC

    For @list (where you're using |), remember that Perl's | operator is the bitwise or operator, e.g., 1 | 2 results in the value '3'. You probably do understand this one, it's just that you're temporarily confused.

    For the difference between the other two, checking1 perlop will yield this nugget "Using 'or' for assignment is unlikely to do what you want; see below."


    1 I'm using Strawberry Perl 5.10.0


    Information about American English usage here and here. Floating point issues? Please read this before posting. — emc

Re: 'or' versus '||' - Unexpected Results
by kyle (Abbot) on Jun 24, 2008 at 16:21 UTC

    For this kind of thing B::Deparse can be a big help.

    perl -MO=Deparse,-p -e 'sub x {return (1,2)} @c=x()||(1,2);@d=x() or ( +1,2);' sub x { return(1, 2); } (@c = (x() || (1, 2))); ((@d = x()) or ('???', '???'));

    You can see here how the precedence difference between || and or make a difference in what happens.

Re: 'or' versus '||' - Unexpected Results
by massa (Hermit) on Jun 24, 2008 at 16:53 UTC
    • my @a = x() | (1, 2, 3) evaluates x() and (1, 2, 3) in scalar context: x() gives 0 and (1, 2, 3) gives 3; now, the result (a single scalar) is put in a list context, resulting in a single-element list, so you have @a = (3) and it warns you that it threw away the (1, 2); it also warns (later, at runtime) that () in scalar context is undef and you are using an undef where you should be using a number
    • my @a = x() || (1, 2, 3) evaluates x() in scalar context and (as x() gives you 0, false) evaluates (1, 2, 3) in list context, so you have @a = (1, 2, 3) and no warnings
    • my @a = x() or (1, 2, 3) has different operator precedence; it could be written as (my @a = x()) || (1, 2, 3) -- it evaluates @a = x() in scalar context and (as x() gives you 0, false) evaluates (1, 2, 3) in void context, so the result of the left hand side of the "or" is (1, 2, 3) and it warns about throwing away the 1, 2 and the 3 :-)
    • my @a = $c->bbox() || (1, 2, 3) evaluates $c->bbox() in scalar context (but in scalar context, bbox returns an array ref!!!) and does not evaluate (1, 2, 3) (it would, in list context, if the left hand side of the "or" were false), so you have @a = ([51,3,102,55]) and no warnings
    • my @a = $c->bbox() or (1, 2, 3) has different operator precedence; it could be written as (my @a = $c->bbox()) || (1, 2, 3) -- it evaluates $c->bbox() in list context, but the whole @a = $c->bbox() in scalar context (the result of said assignment is 4, the lenght of the @a array now), so it will evaluate the (1, 2, 3) in void context, so the result of the left hand side of the "or" is (1, 2, 3) and it warns about throwing away the 1, 2 and the 3 :-)
    So, what you really want is:
    my @a = $c->bbox(); @a = (1, 2, 3) unless @a;

    I don't have 5.10 installed here, so, I can't check if the "//" operator would do the trick for you... HTH!
Re: 'or' versus '||' - Unexpected Results
by pjotrik (Friar) on Jun 24, 2008 at 16:13 UTC
    LIST: 0 | 3 = 3 (number of elements in the array)
    LIST1: unlike ||, 'or' has a smaller priority than the assignment(=). So first 'my @list1 = x()' is executed, then if the it doesn't evaluate to true, '(1,2,3)' is evaluated and thrown away - this is the useless constant on line 14
    I'm not sure what 2 and 3 do as I'm not familiar with Tk, but apparently bbox returns some value which prevents the rest from being evaluated.

      LIST: 0 | 3 = 3 (number of elements in the array)

      That's wrong. For starters, there's no array involved. And before you say "I meant list", there's no list either involved either.

      Like I said in my post, "|" operates on scalars, so

      my @a = x() | (4,5,6);

      is evaluated as

      my @a = scalar(x()) | scalar(4,5,6);

      And keeping in mind that the comma operator returns it's RHS in scalar context,

      my @a = scalar(x()) | scalar(4,5,6); === my @a = scalar(x()) | scalar(5,6); === my @a = scalar(x()) | scalar(6); === my @a = scalar(x()) | 6;

      Perl doesn't even bother keeping the 4 and 5 in the compiled code:

      >perl -MO=Deparse -e"my @a = x() | (4,5,6);" my(@a) = x() | ('???', '???', 6);
        Thanks for the correction.
Re: 'or' versus '||' - Unexpected Results
by Anonymous Monk on Jun 25, 2008 at 08:43 UTC