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

The code I am interested in is the bolded line.
It seems to do just what it looks like it would, but I don't know how or why, so I figure I will shoot myself in the foot with it someday.
Can anyone explain this use of a comma? Does 'or' force a list context on the right hand side, and 'or' is an operator with a lower precedence than ','?
Is there a better or more idomatic way to achieve this result?

Thanks

while ( my ($key, $hashref) = each %HoH) {
DO_SOMETHING or warn("some warning"), next;
}

Replies are listed 'Best First'.
Re: using 'or' followed by ','
by Zaxo (Archbishop) on Jul 22, 2005 at 02:01 UTC

    That comma is the sequence operator in scalar context. As in C, the left hand argument is evaluated, then the right, unconditionally. The comma expression returns the value of the right hand argument.

    Precedence causes the expressions in that statement to bind like this:     DO_SOMETHING or (warn("some warning"), next); If DO_SOMETHING evaluates false, the warning is issued and next is (uselessly, here) called.

    The sequence operator is placed in scalar context by the logical or. In list context, the comma op evaluates and returns a list of both left and right values. That's familiar from providing arguments to subs and initializing arrays. The scalar comma can be thought of as similar to the '||' and '&&' logical ops, differing by the unconditional evaluation of the right hand argument.

    After Compline,
    Zaxo

Re: using 'or' followed by ','
by tlm (Prior) on Jul 22, 2005 at 01:54 UTC

    It's the comma operator. In this case, the comma expression is evaluated in a scalar context (dictated by the or), but still each argument is evaluated; that's the intent here (i.e. the ultimate return value of the expression is not used). A more straightforward alternative for that line would be

    if ( !DO_SOMETHING ) { warn("some warning"); next; }
    or
    unless ( DO_SOMETHING ) { warn("some warning"); next; }

    the lowliest monk

      yet another alternative using "do" so as to maintain the "reversed" syntax:
      do { warn("some warning"); next } unless DO_SOMETHING;
Re: using 'or' followed by ','
by graff (Chancellor) on Jul 22, 2005 at 02:19 UTC
    It's interesting that the "warn" part won't work without the parens, but the "next" works in any case -- consider:
    %h=qw/a 1 b 0 c 2/; while (($k,$v)=each %h) { $x=$v or warn("got a false value for key $k"),next; # $x=$v or warn "got a false value for key $k", next; print "got a true value for key $k: $v\n"; }
    When the parens are there, you get the warning. When you comment the line with parens and uncomment the other, you still get only two lines printed to STDOUT, but nothing printed to STDERR.

    Apparently, when the parens are removed, the comma-separated things after "warn" are grouped and treated as args passed to warn -- and the second arg happens to be an expression that must be evaluated in order to pass the resulting value as an arg. But evaluation of "next" as the second arg has the effect of jumping directly to the next loop iteration -- so warn never gets called.

    By putting parens around the single arg to "warn", you make sure that this expression is fully evaluated, and then the "next" expression is also evaluated, causing the jump to the next iteration. The comma binds tighter than "or", so the expressions separated by commas are evaluated as if they were a block relative to "or".

      This will work:

      $x=$v or (warn "got a false value for key $k"), next;

      As it puts the precedence back to order.

Re: using 'or' followed by ','
by pg (Canon) on Jul 22, 2005 at 02:18 UTC

    One thing you need to know is the precedence between "or" and ",". Perl says "," has higer precedence. This is why "1 or warn("1"), warn("2")" does not print anything, otherwise, you would most likely see "2" gets printed.

    That "next" appears to be decorative, unless that is not the last line in the loop as you showed.