Beefy Boxes and Bandwidth Generously Provided by pair Networks
laziness, impatience, and hubris
 
PerlMonks  

$i=$i++

by Anonymous Monk
on Apr 29, 2002 at 15:14 UTC ( [id://162835]=perlquestion: print w/replies, xml ) Need Help??

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

Hi, I tried for fun what happend when i do
$i=$i++
and noticed it (unlike gcc) evaluated to 0. Now i tried
$i= 0 + $i++;$i = ++$i + $i++;
and that gave $i=0, i then tried
$i = ++$i + $i++;
and that gave 3. My last test really confused me:
$i = $i++ + ++$i
it evaluated to 2.
I fail to see the consistensy here, can someone please enlighten me?
/jon

Replies are listed 'Best First'.
Re: $i=$i++
by Ovid (Cardinal) on Apr 29, 2002 at 15:27 UTC

    You have to understand how expressions evaluate. The pre- and post-increment (and decrement) operators change the variables value, but the variable plus the operator constitute and expression who's value varies depending upon the placement of the ++. Essentially, the value of the expression $i++ is original value of $i and $i is incremented by 1. The value of the expression ++$i is new value of $i after $i is incremented by 1.

    $i = ++$i + $i++;

    In the above example, we evaluate the expression right to left. The expression, $i++, evaluates as zero, even though $i is now 1. The second expression, ++$i, evaluates as 2 (since $i is now 1) and 2+1 equals 3.

    $i = $i++ + ++$i;

    Pretty straightforward now. The rightmost ++$i evaluates as 1 and $i is set to one. The $i++ also evaluates to 1 (remember, with a post-increment operator doesn't change the return value of the expression, just the variable -- confused yet? :). So, 1 + 1 equals two.

    Cheers,
    Ovid

    Update: Got the concept right, but I evaluated in reverse order. Whoops! Juerd is right.

    Update2: Okay, I'm smoking crack. If things are evaluated left to right, then the following is true:

    $ perl -e 'print 1-3+2' 0

    Well, it turns out that this is true. If it were evaluated right to left, then the answer would negative four. What I think is going on (though I can't prove it offhand) is that built-in unary operators are evaluated right to left, and then the rest of the expression is evaluated based upon precedence rules, if any. Anyone want to shed light on this?

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

      In the above example, we evaluate the expression right to left.

      Is that always, or just in this case? When are expressions evaluated left-to-right, and when right-to-left? And why is that so? I would have thought that do { $i = 0; ++$i + $i++; } would evaluate to 2 because (0 + 1) + 1 == 2, but apparently, I'm wrong.

      Update: Got the concept right, but I evaluated in reverse order. Whoops! Juerd is right.

      Answer to Ovid's update: No, I'm not, as do { $i = 0; ++$i + $i++ } does NOT equal 2 -- the right-to-left apparently is correct, I'm just wondering why it is that way.

      - Yes, I reinvent wheels.
      - Spam: Visit eurotraQ.
      

      If it were evaluated right to left, then the answer would negative four

      No, it's a per-operator thing, as + and - are different operators, and their precedence defines they should be evaluated left-to-right. This is done by implicit parentheses:

      2;0 juerd@ouranos:~$ perl -MO=Deparse,-p -e'sub one { 1 } sub two { 2 +} sub three { 3 } print one() - three() + two()' sub one { 1; } sub two { 2; } sub three { 3; } print(((one() - three()) + two()));
      (I had to use these subs, because Perl optimizes 1-2+3 to a literal 0 :)

      So we have these calculation:
      1. 1 - 3 = -2
      2. answer ( = -2 ) + 2 = 0
      But which are evaluated first? the 1 or the 3? The -2 or the 2? It becomes clear when you test the operators one by one (or so I thought):
      2;0 juerd@ouranos:~$ perl -le'sub foo { print "foo"; return 1 } sub ba +r { print "bar"; return 2 } print foo + bar' bar foo 1 2;0 juerd@ouranos:~$ perl -le'sub foo { print "foo"; return 1 } sub ba +r { print "bar"; return 2 } print foo && bar' foo bar 2
      So I decided to test a lot of operator this way, but found out that everything was evaluated left-to-right, but not if I left out the parens. Why did my first test print bar before foo then? Because I didn't print foo() + bar() but did print foo( +bar() ).

      So that still doesn't solve our mistery...

      - Yes, I reinvent wheels.
      - Spam: Visit eurotraQ.
      

(tye)Re: $i=$i++
by tye (Sage) on Apr 29, 2002 at 15:46 UTC

    Ovid offers a plausible explanation for the behavior. However, relying on specific results from such expressions is a very bad idea. Subtle changes to Perl could easily change the results and break any programs that depend on them. Most Perl developers would consider a program that depends on the results of such expressions to already be broken.

            - tye (but my friends call me "Tye")
Re: Increment Operator(s)
by tadman (Prior) on Apr 29, 2002 at 16:46 UTC
    Perl and GCC seem to be on different wavelengths, that's for sure. I'd tend to agree more with the Perl implementation than the GCC one, but the GCC one probably has a good reason for being that way. In either case, you are heading into some pretty dangerous territory, not unlike Star Trek when they travel in a nebula. Smoke comes out of things that smoke isn't supposed to come out of, and random objects in the bridge blow up for no apparent reason.

    Here's a comparison of some typical behaviour:
    Expression Perl GCC ---------------------------- x = ++x 1 1 x = x++ 0 1 x = x++ + x++ 1 1 x = x++ + ++x 2 3 x = ++x + x++ 3 3 x += x++ 1 1 x -= ++x 0 0
    It is clear there is a slight difference as to when the trailing increment is actually executed.
Re: $i=$i++
by TheHobbit (Pilgrim) on Apr 29, 2002 at 16:20 UTC

    Hi,
    the problem is that, basically, this kind of expression does not make sense nor it has a natural value. Post- and pre-increment are special case of the so-called side-effect expression, i.e. expression whose evaluation changes somehow the value of the variables involved.

    Now, in C it is said plainly that you must not rely on the order of evaluation of the sub expressions. I remember an example running on an old machine in which the same expression (with side effects) gave a different result when compiled with the native compiler and when compiled with gcc.

    IIRC, with perl is the same thing, because the optimizer is allowed to change the order in which your sub expressions are evaluated (except of course when the operator is one of the short-cut ones).

    So, never forget the simple rule:

    Never, ever, put more than one side-effect expression affecting the same variable into one expression.

    Cheers
    Leo TheHobbit
Re: $i=$i++
by dsb (Chaplain) on Apr 29, 2002 at 15:41 UTC
    ++Ovid, but I have one more point.

    I ran the following code:

    $i = $i++; print "Test 1: $i\n"; $i = 0; $i = 0 + $i++; $i = ++$i + $i++; print "Test 2: $i\n"; $i = 0; $i = ++$i + $i++; print "Test 3: $i\n"; $i = 0; $i = $i++ + ++$i; print "Test 4: $i\n";
    And got the following output:
    Test 1: 0 Test 2: 3 Test 3: 3 Test 4: 2
    Remebering what Ovid said about pre vs. post increment, when you examine this output you may realize that by the time the print statements in Test2, Test3, and Test4 have been executed, $i has actually been incremented one last time by the post increment operator.

    So if you thought your output was weird maybe this has something to do with it.

    UPDATE: I was going through my posts and found this one. The final incrementation I mention I believe is ultimately irrelevant since the expression's return value i assigned to $i thereby over-writing the value $i has after the ++ operator.




    Amel
Re: $i=$i++
by Elian (Parson) on Apr 29, 2002 at 17:17 UTC
    The order an expression is evaluated in isn't generally defined anywhere for perl. Don't count on evaluation order for things like this, as you may well find yourself surprised when moving from one version of perl to another. (Same goes for access times for tied variables--you can't be sure when in a statement a tied variable will be fetched or stored)
Re: $i=$i++
by extremely (Priest) on Apr 29, 2002 at 16:37 UTC
    Different versions and different compilers can cause these expressions to evaluate differently. The behavior in these cases is specifically undefined. It came up last year on PM. It has come up before on p5p as well.

    --
    $you = new YOU;
    honk() if $you->love(perl)

Re: $i=$i++
by rinceWind (Monsignor) on Apr 29, 2002 at 16:16 UTC
    I've come across much the same conundra in C. However, in the case of C, there was one instance that was platform dependent. It involved subscripts. Here is the snippet if I remember it correctly:
    array[i++] = array[i++] + i;
    I actually found this in a piece of application code. Horrid, isn't it.

    What would the perl equivalent do? I guess it would be consistent across platforms, since perl evaluates expressions in the same order on all platforms.

Re: $i=$i++
by ariels (Curate) on Apr 30, 2002 at 08:17 UTC

    To expand on tye: What gcc does for "i=i++;" is conforming behaviour for ISO C. What any implementation does for it is conforming!

    This sort of code (technically, modifying a variable more than once between sequence points) is specifically said by the standard to evoke undefined behaviour (see the comp.lang.c FAQ). In the comp.lang.c.* newsfroups, it is said that the compiler could compile code which causes daemons to fly out of your nose (aka nasal daemons)! It could also choose to reformat your hard disks except on Friday the 13th, return the value 17, or do anything else.

    Is this "broken"? Not really. By not specifying evaluation order more precisely, the standard allows the compiler a lot more latitude in compiling efficient code. This, at the price of disallowing some essentially useless code. (Would you be happier if the standard said "i=i++; is guaranteed to set the value of i to -29"? Would you use it?)

    Note that all this applies to code generation, not to parsing. That "i++ - ++i + i" is parsed as "(i++ - ++i) + i" has nothing to do with the code emitted by the compiler. Parsing has no idea of "the same" object; that is part of the semantics of C, not its syntax. But not everything illegal is a syntax error!

    Perl has not had such a rigourous standardization. Furthermore, only one implementation exists at any time (assuming we decide 5.004, 5.005, 5.00503, 5.6.0, 5.6.1, and the rest are all different). So it is very tempting to apply the "test and see" engineering technique.

    I'd join tye in recommending you avoid this. First, because it isn't documented behaviour. Unfortunately, Perl has loads of behaviour that couldn't really be said to be "documented" and still gets used. The thing is, these other properties are actually useful. The syntax for them evokes a specific interpretation. Can you really give a good argument for compiling "(++A)+(B++)" (legal code, if A and B are different objects) as any of the following codes, rather than any of the others?

    1. LOAD R1, A INCR R1 LOAD R2, B ADD R3, R1, R2 INCR R2 STORE A, R1 STORE B, R2
    2. LOAD R1, A INCR R1 STORE A, R1 LOAD R2, B ADD R3, R1, R2 INCR R2 STORE B, R2
    Note that if A and B happen to be the same object, you get different results...

      Hi, thanks for the extensive reply. It was just what I was after. I am fully aware that this is code that shouldn't be used, I was just curious why it differed as addition shouldn't care about left/right side.
      /jon
How about this for $i=$i++
by jynx (Priest) on Apr 30, 2002 at 01:54 UTC

    This is probably completely wrong, but...

    What i think is happening is this: the reference to the variable $i is being modified by the auto-(in|de)crement operators before the addition operation because of the precedence table. The reference then gets updated for the left $i for a right-side auto-increment because we're dealing with a reference to the actual variable, not a hard-coded number -- and in the case of the post-increment, because of a bug.

    Examples:
    Let's say we have the expression

    $a + ++$a
    This should leave $a at the value 1, but it should evaluate to 1. It evaluates to 2! Because the pre-increment updates the reference to $a on the left side of the addition operator as well. This is fine if it gets documented, but the following is a straight bug:
    0 + $a++
    We get a return value of 0, and $a is set to 1 as it should be. However if $a is on the left side:
    $a + $a++
    We get a return value of 1! $a is still set to 1 as it should be. If instead of $a we use some other (zeroed) variable it evaluates to 0 correctly. Somewhere Perl is messing up what happens on a right-side post-increment if the same variable is being added to itself. Maybe it's adding 1 to the left side even if it's a post-increment?

    So, to explain the strange ++$a + $a++ i think it works like this:

    1. the code is parsed, the ++'s get the precedence to go first
    2. addition being a left-wise operator, ++$a gets evaluated first; $a gets ++'d to 1
    3. $a++ generates a post-decrement after the statement is over and increments the left-side (erroneously)
    4. the values (2 + 1?) are added returning 3
    5. the post-decrement code is run, and $a is now 2

    i could be wrong :/

    Here's what i ran with the results i obtained:

    #!/usr/bin/perl -w use strict; for $b ( '$a++ ', '0 + $a++', '0 + ++$a', '++$a + $a', '$a + ++$a', '$a + $a++', '++$a + $a++', '$a++ + ++$a', '$a+=$a++' ) { $a = 0; print "$b\t=", eval($b), "\t\$a= $a\n"; } __END__ # results: $a++ =0 $a= 1 0 + $a++ =0 $a= 1 0 + ++$a =1 $a= 1 ++$a + $a =2 $a= 1 $a + ++$a =2 $a= 1 $a + $a++ =1 $a= 1 ++$a + $a++ =3 $a= 2 $a++ + ++$a =2 $a= 2 $a+=$a++ =1 $a= 1
    That's a bug imho (though i could be wrong; better explenations anyone?)

    hope this helps,
    jynx

    update: Hmmm, even stranger.

    If we take

    $a + $a + $a++ # = 0 (correct) $a + $a + ++$a # = 1 (correct) $a + $a++ + $a # = 2 (the trailing $a has no effect maybe?) $a + ++$a + $a # = 3 (the ++ effects previous $a as before?)
    Interesting...

    So the first addition is probably becoming zero before the second addition is messing it up? And to further confuse things:

    $a + $a + ++$a + $a++ # = 2 (correct!) 0 + ++$a + $a++ # = 3 (d'oh!)
    i think at this point i've gone too far...
Re: $i=$i++
by japhy (Canon) on Apr 30, 2002 at 15:57 UTC
    After being baffled for minutes on end about the seeming madness of ++$X + $X++ being 3, I divined the reason:
    1. $X is 0
    2. ++$X increments $X's value, and returns the variable $X (not its value)
    3. $X++ return's $X's value and then increments it, meaning $X is now 2
    4. 2 + 1 = 3
    This can be verified by the Perl debugger:
    perl -de0 main::(-e:1): 0 DB<1> x $N = 0; ++$N, $N++ 0 2 1 1

    _____________________________________________________
    Jeff[japhy]Pinyan: Perl, regex, and perl hacker, who'd like a (from-home) job
    s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??;

      Right. The ultimate reason is that perl does everything by reference internally, not by value. We don't deal with real numbers or strings, we deal with pointers to perl variables. Since you're reusing those pointers multiple times, and affecting them multiple times as well, it's up in the air what the ultimate value will be. (Depends on how perl builds and evaluates the expression)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://162835]
Approved by mandog
Front-paged by rob_au
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others having an uproarious good time at the Monastery: (3)
As of 2024-04-25 12:13 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found