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

Order of operations, mutators, aliasing and whammies

by demerphq (Chancellor)
on Sep 04, 2003 at 22:22 UTC ( [id://289044]=perlmeditation: print w/replies, xml ) Need Help??

Today I was working on something and needed to print out a value, and the value times four, while incrementing the value by four every time. It was during dev/test so I was tweaking and running and extending and tweaking so the code evolved in strange ways. (Er the strangeness may also have to do with that perl programmers (bad)habit of trying to do a million things in one statement.) In the end it resulted in code like the printf line below

print "order1.pl\n"; print 'printf "%4d : %3d\n",$ofs,(($ofs+=4)-4)*4',$/; for ($ofs=0;$ofs<10;) { printf "%4d : %3d\n",$ofs,(($ofs+=4)-4)*4 }

Now, before you get all hot to trot and point out there are a zillion ways to write this more intelligently please understand that this is a reduction of the situation to its minimal case. It wasnt within a for loop, and in the end it all got complete refactored into something much more reasonable than what I am discussing here.

So whats the issue you say? Well the code doesnt work as I expected it to. It prints out the following:

order1.pl printf "%4d : %3d\n",$ofs,(($ofs+=4)-4)*4 4 : 0 8 : 16 12 : 32

Which is not what I expected. But the following code with only a tiny and apparently meaningless change makes it work as expected

print "order2.pl\n"; print 'printf "%4d : %3d\n",$ofs-0,(($ofs+=4)-4)*4',$/; for ($ofs=0;$ofs<10;) { printf "%4d : %3d\n",$ofs-0,(($ofs+=4)-4)*4 }

Which outputs

order2.pl printf "%4d : %3d\n",$ofs-0,(($ofs+=4)-4)*4 0 : 0 4 : 16 8 : 32

Which is exactly as expected. The change of the first parameter from $ofs to $ofs-0 causes it work "correctly".

Now this behaviour confused me. Why should the extra meaningless subtraction affect the order of operations? Well I had a poke around, and from what I could tell the order of operations doesnt change. So what gives?

Well, after a while I remembered that vars are aliased in perl. printf gets whichever variable it was passed by alias, not by copy. So with the first variant the _variable_ $ofs gets put on the stack, then its incremented when evaluating the second expression to go onto the stack. Whereas the second case the expression $ofs-0 is evaluated and then the result is placed on the stack.

Now as a last point, im not real sure that there is any guaranty that the parameters will be evaluated in any given order. I assume its left to right only because of DWIM.

All told this was a case of outsmarting myself, and a good reason to avoid overly complicated constructs in a misplaced attempt at brevity. I spent way longer figuring out what was going on by pulling out B::Terse and friends (although admittedly without much success) than I ever would have if i had rewritten the statement as several. (Er, I did learn one or two things so it wasnt all wasted :-)

Anyway, hope you find this interesting, and hope it saves somebody the time I wasted figuring it out writing it up. :-)


---
demerphq

<Elian> And I do take a kind of perverse pleasure in having an OO assembly language...

Replies are listed 'Best First'.
Re: Order of operations, mutators, aliasing and whammies
by Abigail-II (Bishop) on Sep 04, 2003 at 23:06 UTC
    Now this behaviour confused me. Why should the extra meaningless subtraction affect the order of operations?

    Well, that's easy. You have more operations! Of course things will change. ;-)

    Without the subtraction, all perl needs to do is remember where the value of $ofs is, and get it when it's time to output the resulting string. Of course, in between, the value gets modified. With the subtraction, perl gets the value of $ofs, subtract 0, and scribbles away the result. That result won't get modified when $ofs gets modified.

    Don't modify a variable and use its value elsewhere in the same expression. Don't modify a variable twice in the same expression. Don't assume Perl has a defined order of evaluation.

    Abigail

      Without the subtraction, all perl needs to do is remember where the value of $ofs is, and get it when it's time to output the resulting string. Of course, in between, the value gets modified. With the subtraction, perl gets the value of $ofs, subtract 0, and scribbles away the result. That result won't get modified when $ofs gets modified.

      Yep. Thats what I tried to say myself, but youv'e done a much better job. :-)

      Don't modify a variable and use its value elsewhere in the same expression. Don't modify a variable twice in the same expression. Don't assume Perl has a defined order of evaluation.

      Yep, its just that Perls loose rules for this stuff make it so easy to do. Given enough rope to hang themselves... :-)


      ---
      demerphq

      <Elian> And I do take a kind of perverse pleasure in having an OO assembly language...
      I disagree with the last piece of advice. Perl does have a defined order of evaluation; it is just not the same as your (or my) assumptions. Imagine that Perl did not have such a defined order of evaluation. How can I trust
      print 5+1;
      always prints 6, and not 5 (and a warning)?

      update: I see that I was confusing precedence and evaluation order.

        Perl does have a defined order of evaluation;

        Do you know something that neither p5p nor comp.lang.perl.misc knows? Order of evaluation has been discussed recently on both forums, and noone one these forums has been able to show that Perl has a defined order of evaluation. Please quote the relevant parts of the documentation that define order of evaluation in general. (Order of evaluation is defined for &&, ||, and, or and comma in scalar context, but not generally).

        How can I trust
        print 5+1;
        always prints 6, and not 5 (and a warning)?

        Precedence garantees that. Just like C, Perl does have well defined precedence and associativity rules. But neither language has a defined order of evaluation.

        Abigail

Re: Order of operations, mutators, aliasing and whammies
by diotalevi (Canon) on Sep 05, 2003 at 01:16 UTC

    I wrote the following program the other day just to see where if anywhere the order of execution is defined within a statement. Its meant to be run from the root of your perl source tree. The net result: nada.

    use strict; use File::Find; find( sub { return 1 if -d; my @chunks = read_file() =~ /^[^\n]*\border\b.{1,30}\bof\b[^\n +]*/gims; return unless @chunks; print "$File::Find::name: @chunks\n\n" }, "." ); sub read_file { local $/; local @ARGV; push @ARGV, (@_ ? @_ : $_); <> }
Re: Order of operations, mutators, aliasing and whammies
by Roger (Parson) on Sep 05, 2003 at 07:45 UTC
    As a Perl rule in general, assignments and operations take precedence over non-assignments.

    In the first case (with out the +0), the precedence for printf "%4d : %3d\n",$ofs,(($ofs+=4)-4)*4 is:

    1 -- (($ofs+=4)-4)*4 2 -- $ofs

    Where the expression contained the assignment of $ofs+=4 has a higher precedence over the expression of $ofs by itself.

    In the second case, both the $ofs+0 and $ofs+=4 has the same precedence, so the $ofs+0 will be evaluated and pushed onto the stack first, before the $ofs+=4 gets evaluated.

      As a Perl rule in general, assignments and operations take precedence over non-assignments.

      Bullocks. Assignment has a low precedence. Perl has 24 levels of precedence for operators, and assignment only has precedence level 19. Furthermore, it's the comma that separates the expressions - what might be relevant is the associativity of the comma operator.

      However, in the shown code, the problem lies neither with precedence or associativity. It lies with order of evaluation. And Perl defines order of evaluation only for a few operators: &&, ||, and, or, and the comma in scalar context.

      Precedence has nothing to do with order of evaluation. Precedence is a compile time thing, it determines how the parse tree looks like. Order of evaluation is a run time thing, and the compiler/interpreter is free to determine the most efficient order of evaluation.

      Abigail

        Thanks for the explanation, I have filled in the hole in my understanding of the order of evaluations. :-D

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others taking refuge in the Monastery: (4)
As of 2024-04-19 07:04 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found