Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical

Puzzling $| behavior

by saintmike (Vicar)
on Oct 07, 2007 at 21:14 UTC ( #643328=perlquestion: print w/replies, xml ) Need Help??
saintmike has asked for the wisdom of the Perl Monks concerning the following question:

Can anyone explain why
$| = 0; print "first=", $|, " second=", $|--, "\n";
prints "first=1 second=0" and
$| = 1; print "first=", $|, " second=", $|--, "\n";
prints "first=0 second=1"?

Replies are listed 'Best First'.
Re: Puzzling $| behavior
by dirving (Friar) on Oct 07, 2007 at 21:45 UTC
    Perl's behavior isn't defined when you read the value of a variable and then auto-increment/decrement it later in the same statement. Try doing this instead and you'll get the behavior you expect:
    $| = 0; print "first=$|,"; $|--; print "second=$|\n"; $| = 1; print "first=$|,"; $|--; print "second=$|\n";

    In your first case, Perl evaluates the second $|, then decrements it, then evaluates the first one, leading to the unexpected behavior. The reason you don't end up with "-1" is that there is magic associated with $|. It doesn't actually hold a value, it just tracks whether it is set to a non-zero value or not. Try for instance  $| = -3; print $| to see this behavior.

    Read perlvar and perlop for more information on what is going on here.

    -- David Irving
Re: Puzzling $| behavior
by ikegami (Pope) on Oct 07, 2007 at 23:16 UTC

    For starters, $|-- is special and may confuse the issue. $|-- toggles $| between 0 and 1. (No idea why)

    Secondly, it's bad to modify and use a variable in the same expression because

    print "first=", $var, " second=", $var--, "\n";

    is basically equivalent to

    do { local @_; alias $_[0] = "first="; alias $_[1] = $var; alias $_[2] = " second="; my $anon = $var--; # <-- Changes $var *and* $_[1] alias $_[3] = $anon; # since $_[1] is an alias alias $_[4] = "\n"; # for $var. &print; };
Re: Puzzling $| behavior
by mwah (Hermit) on Oct 07, 2007 at 21:56 UTC
    Yesterday I wrote the following "opinion":

    | print gets its "arguments in reverse order", similar to C.
    | The above call would (in C) look like
    | ...
    | XPUSH( "\n" );
    | XPUSH( $| );
    | $|--;               # plus true/false magic
    | XPUSH( "second=" );
    | XPUSH( $| );
    | XPUSH( "first=" );
    | PUTBACK;
    | call_pv("print", G_ARRAY);
    | ...
    | (please correct here me if I'm wrong)

    which turned out to be completely wrong.
    After reading the other posts and looking further
    into the topic, I came to the conclusion that perl
    evaluates the argument list before calling into a sub-
    routine always left-to-right.

    This can be shown by reading the deparsed output
    from B::Bblock /1/. If used on the following lines:
    1: $| = 1; 2: print "first=", $|, " second=", $|--, "\n";
    the important output will be:
    OP (0x824de58) enter COP (0x81f3290) nextstate #1 $| = 1 SVOP (0x824dc80) const [6] IV (0x8167cdc) 1 PADOP (0x818ae48) gvsv GV (0x8168894) *| BINOP (0x8189398) sassign # COP (0x824c638) nextstate OP (0x8250178) pushmark # 2 SVOP (0x8189428) const [7] PV (0x816881c) "first=" PADOP (0x818c6f0) gvsv GV (0x8168894) *| SVOP (0x818de00) const [8] PV (0x8168828) " second=" PADOP (0x824ff88) gvsv GV (0x8168894) *| UNOP (0x824fe50) postdec [4] SVOP (0x824de38) const [9] PV (0x81688b8) "\n" LISTOP (0x8250150) print # LISTOP (0x824dac0) leave [1]
    from where it can be learned that the $| decrement
    occurs (postdec [4]) where it should.

    Sorry for writing such a mess without checking before :-(



    /1/ perl -MO=Bblock
      (please correct here me if I'm wrong)
      I don't know if perl's documentation defines (as a feature) the order in which arguments are evaluated, but it's simple to check that it really does evaluate them in left-to-right order:
      $ perl -le 'print sub{print 1; "x"}->(), sub{print 2; "y"}->()' 1 2 xy
      The reason the OP's example is weird is because Perl's argument passing uses aliases whenever possible (as ikegami illustrates below). In the example, an alias to $| gets passed as the second argument. When evaluating the fourth argument, $| is changed. When print finally inspects its arguments, the aliased second argument will report the changed value.


        I don't know if perl's documentation defines (as a feature) the order in which arguments are evaluated

        It's not, but no one has mentioned a system or version where the arguments are evaluated in any order other than left-to-right in past discussions on the subject.

        It's technically subject to change since it's not documented, but I find it highly unlikely to change in Perl5.

Re: Puzzling $| behavior
by cosmicperl (Chaplain) on Oct 07, 2007 at 23:17 UTC
    $| is either 1 or 1. If it's 0 and you are telling it to change value with $|--, it's go to 1. If this isn't what you want seems to me an if statement is in order. Lyle
Re: Puzzling $| behavior
by Anonymous Monk on Oct 08, 2007 at 07:24 UTC
    Just for fun, try this:
    $x=0; printf "%d %d %d\n",$x--,$x--,$x--;
    Then try the same code (syntax adjusted) in any other language that supprts printf, like C. You will get different answers depending on the implementation. Some compilers give gifferent results depending on optimisation switches (Visual Studio, not gcc).
    A good reason not to nest ++ or -- in other statements?

      The OP's problem is not (just) due to the order in which the operands are executed (left to right). Try

      $x=0; printf "%d %d %d\n",$x,$x--,$x;

      Note that even though Perl executed the four operand from left to right, the result is -1 -1 -1.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://643328]
Approved by Corion
Front-paged by Corion
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others wandering the Monastery: (7)
As of 2017-12-11 22:01 GMT
Find Nodes?
    Voting Booth?
    What programming language do you hate the most?

    Results (314 votes). Check out past polls.