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

Hi

I'm perplexed; I'm using CGI to receive a list of parameters, which might arrive under one name or another. So I wrote:

my @things = $q->param('t') or $q->param('things');

I wanted the list of parameters in t, or if that parameter is absent, the ones in things. It's all good if t is present, but fails otherwise, with an empty list. I narrowed it down to the following test case:

sub foo { wantarray ? () : 1; } sub bar { wantarray ? (3, 4) : 3; } my @thingies = foo() or bar(); warn @thingies; # Expected (3,4) here, got () my @thingies = ( foo() ) or ( bar() ); warn @thingies; # Expected (3,4) here too, got ()

I don't understand! If I were using ||, sure, that puts the calls in scalar context; however, with or, why doesn't the first sub get called in list context, return false with the empty list, and let the second sub get called? What am I missing?

Thanks
ViceRaid

update: fixed cut-and-paste mistakio in code results comment, as pointed out by Abigail-II

Replies are listed 'Best First'.
Re: Precendence and wantarray puzzling me
by broquaint (Abbot) on Mar 04, 2004 at 15:47 UTC
    You really want || there as the = binds tighter than the or e.g
    perl -MO=Deparse - my @things = $q->param('t') or $q->param('things'); my @things = $q->param('t') || $q->param('things'); ^D $q->param('things') unless my(@things) = $q->param('t'); my(@things) = $q->param('t') || $q->param('things');
    Your best option for conditionally assigning lists is the ternary conditional operator e.g
    my @things = $q->param('t') ? $q->param('t') : $q->param('things');
    HTH

    _________
    broquaint

      Ah, thanks all. I was put off by the fact that:

      my @things = $q->param('t') || $q->param('things');

      didn't DWIM, as the || puts each method call in scalar context. As an aside, how could one force list context for the evaluation of each of those method calls? With my imaginary list counterpart to scalar:

      my @things = list($q->param('t')) || list($q->param('things'));
        It can't be done (forcing list context with || that is), hence the ?: suggestion.
        HTH

        _________
        broquaint

Re: Precendence and wantarray puzzling me
by borisz (Canon) on Mar 04, 2004 at 15:43 UTC
    your code does this:
    (my @things = $q->param('t')) or $q->param('things')
    Boris
Re: Precendence and wantarray puzzling me
by matija (Priest) on Mar 04, 2004 at 15:48 UTC
    The "or" operator has very, very low precedence - even lower than the assignment operator ("=").

    perldoc perlop says:

    $a = $b or $c; # bug: this is wrong ($a = $b) or $c; # really means this $a = $b || $c; # better written this way
Re: Precendence and wantarray puzzling me
by Abigail-II (Bishop) on Mar 04, 2004 at 15:53 UTC
    my @thingies = foo() or bar(); warn @thingies; # Expected (3,4) here, got (1)
    You got (1)? That would be a bug. I get an empty list, as I expect. foo() returns an empty list, which is assigned to @thingies. bar() is called, but its result is discarded.
    my @thingies = ( foo() ) or ( bar() ); warn @thingies; # Expected (3,4) here too, got ()
    And rightly so. This is exactly the same expression as the first one. The parens are for precedence only, and a fairly trivial one.
    however, with or, why doesn't the first sub get called in list context, return false with the empty list, and let the second sub get called?
    But this is exactly what is happening! Perhaps you don't realize the difference between or and ||: precedence. or has a low precedence, lower than assignment. Perhaps you want:
    my @thingies = foo (); @thingies = bar () unless @thingies;

    Abigail

      my @thingies = foo (); @thingies = bar () unless @thingies;
      When I first saw that, I thought to myself "Couldn't that unless @thingies modifer be replaced by an ||=?" So I tried it, perl -e"@x=qw/foo bar/; @x ||= qw/baz/; print @x" And got this lovely error: Can't modify array dereference in logical or assignment (||=) at -e line 1, near "qw/baz/;". Can anyone explain why ||= doesn't work with an array, and what that error means?
        EXPR1 ||= EXPR2 is sort of a super charged EXPR1 = EXPR1 || EXPR2. However, if EXPR1 is an array (say @a), it would expand to: @a = @a || EXPR2. Nothing wrong with the latter, but the two @a's are quite different. The one on the left hand side is an lvalue in list context, the one on right hand side of the assignment is in scalar context. However, EXPR1 ||= EXPR2 isn't syntactic sugar for EXPR1 = EXPR1 || EXPR2, because in the latter, EXPR1 is executed twice, in the former, just once. But if EXPR1 in EXPR1 ||= EXPR2 is only going to be executed once, what should it be? The scalar value, or the list value? Both are needed.

        Abigail