Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Re: Pre vs Post Incrementing variables

by BrowserUk (Patriarch)
on Sep 12, 2010 at 09:42 UTC ( [id://859826]=note: print w/replies, xml ) Need Help??


in reply to Pre vs Post Incrementing variables

Because long, long ago, in a land far away, the designer's of a language wrote in their specification:

The precedence and associativity of all the expression operators is summarised in section 18. Otherwise, the order of evaluations of expressions is undefined. In particular the compiler considers itself(*) free to compute sub-expressions in the order it believes(*) most efficient, even if the sub-expressions involve side effects. The order in which side effects take place is unspecified. Expressions involving a commutative and associative operator (*, +, &, |, ^) may be rearranged arbitrarily, even in the presence of parentheses; to force a particular order of evaluation, an explicit temporary must be used.

(*) Who knew they had sentient software way back then :)

Essentially, the designers of the C language traded source code ambiguity, programmer intuition, and program correctness, for performance.

Instead of allowing the programmer to specify exactly the order in which sub-expressions would be executed--through the intuitive use of parentheses and precedence, rather than clumsy hack of creating unnecessary temporary variables--they opted to allow compiler writers to re-order those sub-expressions "for efficiency".

Perhaps the most short-sighted and pervasive premature optimisation ever.

In the days when cpu clock speeds were measured in low megahertz and single opcodes could take dozens of those clock cycles to perform; in a language that was explicitly designed to compiled directly to machine code; such optimisations could have a significant impact on program performance. So programmers would tolerate such inconveniences, for the gains that could be achieved.

Now, it is arguable that the trade-off no longer makes sense: when you have processors with speeds measured in Gigahertz executing multiple instruction per clock cycle; in a language were even the simplest of sub-expressions takes dozens if not hundreds of op-codes. The scope for performance optimisation through the reordering of sub-expressions is essentially non-existent.

But, for better or worse, Perl apes many of the rules laid down by the C language. In part I believe, because it made things easier for C programmers moving to Perl. So, we have the situation whereby the order of sub-expression evaluation is "unspecified", meaning that you cannot predict the result of using two or more side-effectful operations on a single variable within the same expression, on the basis of the language description alone.

It also means that when ambiguities arise in the implementation, such as the one you describe, you cannot point a finger and called it a bug, because "the behaviour is unspecified". You'll simply be told: "Don't do that!".

The fact that there is no logical reason, performance or otherwise, for having pre- and post-increment operate differently in this regard:

$n=0; print ++$n, ++$n, ++$n, ++$n;; 4 4 4 4 $n=0; print $n++, $n++, $n++, $n++;; 0 1 2 3

is simply dismissed as user error. "Because you shouldn't be doing it anyway!".

One day, someone will see through these hoary ol' chestnuts of programming lore, and define a language that allows the programmer to write source code that specifies exactly what he wants to happen, and know it will happen. Without having to recourse to inventing unnameable intermediary variables for no good reason.

Till then, you'll just have to get used to not using two or more side-effectful operators, on the same variable, within the same statement. {shrug}


Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.

Replies are listed 'Best First'.
Re^2: Pre vs Post Incrementing variables
by ikegami (Patriarch) on Sep 12, 2010 at 16:12 UTC

    It's got nothing to do with operand evaluation order. It has to do with pre-increment returning lvalues.

    f(++$i, ++$i)
    is more or less equivalent to
    do { local @_; alias $_[0] = ++$i; alias $_[1] = ++$i; &f; }

    Since the pre-increment operator returns the variable itself and not a copy, the above is the equivalent to

    do { local @_; ++$i; alias $_[0] = $i; ++$i; alias $_[1] = $i; &f; }

    As you see, f() sees the same value for both arguments as both arguments are the same variable.

      It's got nothing to do with operand evaluation order.

      Who said anything about operand evaluation order? (How could there be an operand evaluation order for a unary operator.)

      I'll come back to this.

      It has to do with pre-increment returning lvalues.
      $i = 0; ++$i = 'fred';; Can't modify preincrement (++) in scalar assignment

      So, not an lvalue.

      is more or less equivalent to ... alias

      Ah! That well know Perl keyword 'alias'....

      It's got nothing to do with operand evaluation order.

      Hm. "It" has everything to do with the fact that the evaluation order of sub-expressions is unspecified.

      Where "it" is the contradictory and useless behaviour, observed by the OP, and repeated in my post.

      Because, if were specified, then the implementation would not be able to get away with producing those totally illogical, useless results, for those unwise or unknowing enough to try and use, what could be a useful behaviour.

      Your attempts to explain how the implementation produces these useless results from this unspecified and therefore deprecated code, does naught to detract from the reason why it has been possible to enshrine this broken behaviour in the implementation.

      The fact that f( ++$n, ++$n ) passes an alias to $n, rather than the value resulting from the preincrement, is just another broken behaviour. It is equivalent to C allowing:

      #include <stdio.h> void f( int *a, int *b ) { printf( "a: %d, b: %d \n", *a, *b ); *a = 1; *b = 2; } int main( int argc, char ** argv ) { int x = 0; f( &( ++x ), &( ++x ) ); return 0; }

      Which it doesn't:

      junk.c junk.c(11) : error C2102: '&' requires l-value junk.c(11) : error C2102: '&' requires l-value junk.c(11) : error C2198: 'f' : too few arguments for call

      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.
          $i = 0; ++$i = 'fred';; Can't modify preincrement (++) in scalar assignment

          So, not an lvalue.

        Hmm. I was looking at this prior to my previous post. Notice that:
        do { my $i = 16; ${ \(++$i) } = 'fred'; $i }; # returns 'fred' do { my $i = 16; ${ \($i++) } = 'fred'; $i }; # returns 17

        Maybe the warning you got is due to how Perl parses the preincrement assignment expression? I don't know.

        Who said anything about operand evaluation order? (How could there be an operand evaluation order for a unary operator.)

        You did, or rather the passage you quoted. The expressions whose order of evaluation is not defined are those that are operands. The order of those that aren't operands — those that are statements — is well defined.

        No, the list operator is not a unary operator.

        So, not an lvalue.

        It appears that Perl doesn't realise that ++$i returns an lvalue (thus the syntax error), but it does.

        $ perl -E'sub { $_[0]="fred"; }->($i++); say $i' 1 $ perl -E'sub { $_[0]="fred"; }->(++$i); say $i' fred

        Ah! That well know Perl keyword 'alias'....

        You understood what that code did, right? Troll. It's actually an imported function, fyi.

        "It" has everything to do with the fact that the evaluation order of sub-expressions is unspecified.

        No, even if "it" was specified — it's currently left to right for all the operators involved — the results would be the same.

        The fact that f( ++$n, ++$n ) passes an alias to $n, rather than the value resulting from the preincrement, is just another broken behaviour.

        That's entirely possible.

        It is equivalent to C allowing:

        Perl passes args by reference and C can't, so I don't see your point. It is equivalent to C++ allowing:

        #include <stdio.h> void f( int &a, int &b ) { printf( "a: %d, b: %d \n", a, b ); a = 3; b = 4; } int main( int argc, char ** argv ) { int x = 0; f( ++x, ++x ); printf( "x: %d \n", x ); return 0; }

        Which it does:

        $ g++ -o a a.cpp && a a: 2, b: 2 x: 4
        The fact that f( ++$n, ++$n ) passes an alias to $n, rather than the value resulting from the preincrement, is just another broken behaviour.
        Note that this behaviour isn't unique to ++.
        sub f {say "@_"} my $n = 1; f $n += 2, $n += 2; __END__ 5 5

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://859826]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (3)
As of 2025-07-12 03:24 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?
    erzuuliAnonymous Monks are no longer allowed to use Super Search, due to an excessive use of this resource by robots.