http://qs321.pair.com?node_id=107171

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

I was trying to patch some buggy code when I came across some syntax that I thought was a bit odd and certainly doesn't return results that I expect. Here's the smallest test case I could replicate:

perl -e '$x=1;print 1 if $x or print 0'

That prints 1, just as I would expect, despite the syntax being a bit odd. However, if I set $x to a false value, it prints 01. Why the heck does it print the zero and then turn around and print the one, anyway? Since print, in this context, should always return a true value, shouldn't this short-circuit and not do both prints? And wouldn't the first print be skipped by the "if $x" check?

If I change this slightly:

my $x = 0; &one if $x or &zero; sub one { print "one"; 1; } sub zero { print "zero"; 0; }

That only prints "zero". I have an explicit return of false in &zero, but all in all, I'm just not quite grokking this.

Cheers,
Ovid

Vote for paco!

Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

Replies are listed 'Best First'.
Re: Weird syntax question
by John M. Dlugosz (Monsignor) on Aug 23, 2001 at 02:37 UTC
    The condition of the if is performed first: $x is false, so it needs the Right hand side of the OR. That prints 0, returns true, and then the if is satisfied (false or true => true). Then it prints 1 because the if said so.

    This implies that the or applies to the condition after the if, not to the statement including the if:

    print 1 if ($x || print 0)
    —John

      Good point. Both operands to an "or" are expressions, not statements, so you can't parse "print 1 if $x or print 0" as "( print 1 if $x ) or print 0" since "print 1 if $x" is not a valid expression (since "if" is a statement modifier).

              - tye (but my friends call me "Tye")
Re: Weird syntax question
by htoug (Deacon) on Aug 23, 2001 at 09:45 UTC
    Whenever you are confused by some wierd perl syntax alway run to the wonderfull B::Deparse module.

    If you use it with the -p option to add extra parentheses it will often (almost always) be clear what is going on.

    Using it on your example I get:

    perl -MO=Deparse,-p -e '$x=1;print 1 if $x or print 0' ($x = 1); (($x || print(0)) and print(1)); -e syntax OK
    It should be clear that all the explanations concerning 'or' and expressions are true.

    Your other question is related.
    The interesting part is the line &one if $x or &zero;B::Deparse deparses it as (($x || &zero) and &one); $x is false, so it must evaluate &zero.
    &zero prints 'zero' and returns false.
    So the entire expression is false and &one is not called.

    To sum it all up: use B::Deparse and all will be revealed.

Re: Weird syntax question
by Zaxo (Archbishop) on Aug 23, 2001 at 02:38 UTC

    print always succeeds (for a suitable definition of 'always'), i.e. print 0; returns 1.

    $ perl -e 'print print 0' 01$

    Update: I should also say that the short-circuit behavior of or omits the print 0 clause when $x is true.

    After Compline,
    Zaxo

      perl -e 'print qq(Gotcha!\n) unless print STDIN qq(Hello)'
      All interactions with the system have the possibility of failure...
Re: Weird syntax question
by rchiav (Deacon) on Aug 23, 2001 at 02:40 UTC
    Through a little poking around, this evaluates as..
    perl -e '$x=1;print 1 if ($x or print 0)'
    Which evalutates  ($x or print 0) first. That will always return true so the 1 gets printed in either case.

    With the subs, it will evaluate to false since you're returning 0.

    Rich

(tye)Re: Weird syntax question
by tye (Sage) on Aug 23, 2001 at 02:39 UTC

    "or print 0" is true unless the print fails so your if ends up being true as well which causes the "print 1" to be executed.

            - tye (but my friends call me "Tye")
Re: Weird syntax question
by flgr (Sexton) on Aug 23, 2001 at 14:19 UTC
    I'd probally do it that way: perl -e '$x=1;print $x'. But that only works with your (maybe cutted) example. If you want to get one out of two strings depending on the value of something use perl -e '$x=1;print $x?'foo':'bar'.

    $§=lc(sub{}^" \6 \n");$§=~s/\(.*\)//;for(split
    ('',$§)){$/=$_;for(5..7){$/++}$:.=$/}print$:;1
Re: Weird syntax question
by Anonymous Monk on Aug 23, 2001 at 22:40 UTC
    What you have in mind is a re-arrangement of:

       if ( $x ) { print 1; } else { print 0; }

    What you are really getting is:

       if ( $x or print 0 ) { print 1; }
    

    Thus when $x is false, 'print 0' is executed, and then, since the print() returns true, the if-then body is carried out.