Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw

Re: Why does the first $c evaluate to the incremented value in [$c, $c += $_] ?

by Eily (Parson)
on Mar 04, 2014 at 21:23 UTC ( #1076961=note: print w/replies, xml ) Need Help??

in reply to Why does the first $c evaluate to the incremented value in [$c, $c += $_] ?

Edit: ikegami is right, I mistook precedence for evaluation order, and just kept being blinded by that mistake. Please read his correct explanation.

If you look at the precedence list in perlop, += is of higher precedence than ,, so it will be run first, just like the multiplication will be run before the addition in $c+$c*$_

In $c+0, $c+=1; + is the highest precedence operation, so addition is indeed run first. In $tmp = $c, $c+=1; = and += are of equivalent precedences, and so are run from left to right (and that's because of the , in between).

Edit: added a ; here and a ; there, because their absence made my post look confusing.

Replies are listed 'Best First'.
Re^2: Why does the first $c evaluate to the incremented value in [$c, $c += $_] ?
by smls (Friar) on Mar 04, 2014 at 23:07 UTC

    Hm, no, I don't think that explains it.

    Operator precedence requires, of course, that the += operator is evaluated before the , operator is evaluated, but it does not explain why the += operator is evaluated before the first argument to the , operator is evaluated.

    And indeed, with other operators (i.e. other than ++ or += and friends), this does not happen. For example, . also has higher precedence than , but it does not cause the second decorate() call to happen before the first in the following example:

    use feature qw(say state); sub decorate { state $counter = 0; return ++$counter . ':' . shift } my @a = ( decorate("foo"), decorate("bar") . "!" ); say "@a"; # prints "1:foo 2:bar!" and not "2:foo 1:bar!"

    If I correctly understand the perlop paragraph quoted by Eily below, it appears that auto-increment operators only participate in the normal evaluation order as far as their return value is concerned, but their side-effect (modifying the variable) happens at an undefined time.

    I really wonder why that is the case, though. Just as a function's side-effects happen when the function call is evaluated (from the point of view of the larger expression), I would have expected the side-effect of += to happen when it's the operator's turn to be evaluated in the evaluation order of the larger expression.

      That's because decorate has an even higher precedence (on the left of any operator except commas, and unless parenthesis are involved) so what happens is actually:

      (decorate("foo"), decorate("bar").'!') ((return $decorated_foo), (return $decorated_bar.'!')) ((return $decorated_foo), (return $decorated_bar_with_exclamation_mark +)) ($decorated_foo, (return $decorated_bar_with_exclamation_mark)) ($decorated_foo, $decorated_bar_with_exclamation_mark)
      (This is of course, not actual code, but just a representation)
      So first the calls to decorate are resolved, then the concatenation, and at last the values are added to the list. But the concatenation does not happen last.

      Edit: "removed" a bit about precedence being higher on the left of some operators, because it's late, and I'm not sure about what I'm saying.

        D'oh... Yeah, you're right.

        So, for the record, the evaluation order is in fact well-defined: It strictly evaluates higher-precedence operators first, before evaluating any operands of any less deeply nested part of the expression.

        That's the source of my confusion: I intuitively expected Perl to only use the operator precedence information to implicitly "add parenthesis" for disambiguation, but then evaluate the expression left-to-right, only recursing into nested sub-expression once they are encountered.

        That's two conceptually very different ways of defining evaluation order, but easy to miss because in practice it probably never makes a difference except in special cases involving += and friends.

        Well, almost never... :) Here's a demonstration of the evaluation order using tied scalars that report when they're being accessed:

        use warnings; use strict; package LoggingScalar { require Tie::Scalar; our @ISA = qw(Tie::StdScalar); sub FETCH { print "fetched: ".(${shift()} // '<undef>')."\n" } } tie my $x, 'LoggingScalar'; tie my $y, 'LoggingScalar'; tie my $z, 'LoggingScalar'; ($x, $y, $z) = qw(x y z); print "---\n"; my @a = ($x, $y . '!', $z);

        ...which outputs:

        --- fetched: y fetched: x fetched: z

        As for the mentioned perlop paragraph re. "undefined behavior", I suppose that refers to what Eily says here.

Re^2: Why does the first $c evaluate to the incremented value in [$c, $c += $_] ?
by hazylife (Monk) on Mar 04, 2014 at 21:47 UTC
    Yep, you're absolutely right Eily, thanks for clarifying this! (ironically, I know that precedence table by heart, and have for some 15 odd years :-)
Re^2: Why does the first $c evaluate to the incremented value in [$c, $c += $_] ?
by ikegami (Pope) on Mar 06, 2014 at 15:36 UTC

    That's completely wrong.

    Precedence indicates where implied parentheses are located. Precedence mere dictates that $c, $c += 1 is short for $c, ($c += 1) rather than ($c, $c) += 1. Precedence is not relevant.

    Operand evaluation order dictates which of $c and $c += 1 is evaluated first. Since operand evaluation order is left-to-right for the list/comma operator, $c is evaluated before $c += 1, so you got the order wrong too.

    You can see the LTR order in the compiled code:

    >perl -MO=Concise,-exec -e"my @a = ( $c, $c += 1 );" 1 <0> enter 2 <;> nextstate(main 1 -e:1) v:{ 3 <0> pushmark s 4 <#> gvsv[*c] s # $c 5 <#> gvsv[*c] s # $c + 1 6 <$> const[IV 1] s # 7 <2> add[t4] sKS/2 # 8 <0> pushmark s 9 <0> padav[@a:1,2] lRM*/LVINTRO a <2> aassign[t5] vKS/COMMON b <@> leave[1 ref] vKP/REFC -e syntax OK

    You can see the LTR order in this example:

    my $c = 4; sub c :lvalue { print("$_[0]\n"); $c } my @a = ( c("a"), c("b") += 1 ); print("@a\n");


    a b 5 5

      Yes you're right. I wish I hadn't been so blind about that confusion. I updated my post, quite late I'm afraid.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://1076961]
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others musing on the Monastery: (2)
As of 2018-01-23 04:37 GMT
Find Nodes?
    Voting Booth?
    How did you see in the new year?

    Results (238 votes). Check out past polls.