in reply to An obscure side effect?

If you implement your own substr (swab2 below) then you get yet another behavior---it always returns '\0B'---but you can see the order things are being called in. The substr's are evaluated left-to-right and are always working on the original string---it seems to do all of the substr's first, then do the assignments. In that case, you'd get:

  char 0: A ^ B ^ A ^ B = \0
  char 1: B ^ A ^ B     = A
which is what you're seeing. On the other hand, if you use assignments to guarantee the order things are evaluated in (swab3 below), you get the results you'd expect.

I think you may simply be running into a case where Perl's order of operations is undefined. It can evaluate xor's, substr's, and assignments in any order it feels like, and it doesn't always feel like doing it the same way.

Here's the code I played with, and the results.

#!/usr/bin/perl -slw sub mysubstr : lvalue { print "mysubstr(@_)"; print "Returning value '",substr($_[0],$_[1],$_[2]),"' as lvalue."; substr($_[0],$_[1],$_[2]); } sub swab{ my $s = "swab$ARGV[0]"; goto &$s; } sub swab1{ substr( $_[0], $_[1], 1 ) ^= substr( $_[0], $_[2], 1 ) ^= substr( $_[0], $_[1], 1 ) ^= substr( $_[0], $_[2], 1 ); } sub swab2{ mysubstr( $_[0], $_[1], 1, "1" ) ^= mysubstr( $_[0], $_[2], 1, "2" ) ^= mysubstr( $_[0], $_[1], 1, "3" ) ^= mysubstr( $_[0], $_[2], 1, "4" ); } sub swab3{ my($c1,$c2); print " Char $_[2]: ",$c2 = substr($_[0],$_[2],1); print " Char $_[1]: ",$c1 = substr($_[0],$_[1],1); print " 1 ^ 2: ",$c1 = $c1 ^ $c2; print " 2 ^ 1 ^ 2: ",$c2 = $c2 ^ $c1; print "1 ^ 2 ^ 2 ^ 2: ",$c1 = $c1 ^ $c2; substr($_[0],$_[1],1)=$c1; substr($_[0],$_[2],1)=$c2; } my $t; $t ='AB'; swab( $t, 0, 1 ); print "'$t'"; print "'$t'"; print "length: ",length($t); $t ='AB'; swab( $t, 0, 1 ); print "'$t'"; print "'$t'"; print "length: ",length($t);
produces:
$ perl /tmp/t55 1            
'BA'
'BA'
length: 2
'A'
'A'
length: 2
$ perl /tmp/t55 2
mysubstr(AB 0 1 1)
Returning value 'A' as lvalue.
mysubstr(AB 1 1 2)
Returning value 'B' as lvalue.
mysubstr(AB 0 1 3)
Returning value 'A' as lvalue.
mysubstr(AB 1 1 4)
Returning value 'B' as lvalue.
'B'
'B'
length: 2
mysubstr(AB 0 1 1)
Returning value 'A' as lvalue.
mysubstr(AB 1 1 2)
Returning value 'B' as lvalue.
mysubstr(AB 0 1 3)
Returning value 'A' as lvalue.
mysubstr(AB 1 1 4)
Returning value 'B' as lvalue.
'B'
'B'
length: 2
$ perl /tmp/t55 3
   Char 1: B
   Char 0: A
        1 ^ 2: 
    2 ^ 1 ^ 2: A
1 ^ 2 ^ 2 ^ 2: B
'BA'
'BA'
length: 2
   Char 1: B
   Char 0: A
        1 ^ 2: 
    2 ^ 1 ^ 2: A
1 ^ 2 ^ 2 ^ 2: B
'BA'
'BA'
length: 2

Replies are listed 'Best First'.
Re: Re: An obscure side effect?
by BrowserUk (Patriarch) on Aug 03, 2003 at 21:00 UTC

    Interesting. I did think about the order of execution thing, and whether that might be the cause, but then I tried this.

    By redefining swab(), the behaviour goes back to the original, expected result for the first time I call it, with a second and any subsequent calls to the redefined swab() producing erroneous effects.

    Then I tried this

    This shows that both instances of the routine produce the correct result the first time they are called, but then erroneous effects the second time.

    This suggests to me that there is an uninitialised piece of memory being used somewhere that was probably initialised to \0 by the malloc or memset that allocated the larger pool of memory from which it was suballocated, but when the routine is called a second time, the same piece of memory is being reallocated from the same pool, but now has whatever last value it contained. Perhaps it is just a C auto that is being allocated on the stack and happens to have a zero in it the first time it is called, but retains the last value it had the next time it gets used?


    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller
    If I understand your problem, I can solve it! Of course, the same can be said for you.

      I haven't been able to find any good, solid information about Perl's order of execution for assignment and operand evaluation. If it is the case that the order is undefined in swab, then if Perl wanted to evaluate it one way on Wednesdays and another on Thursdays, unless it was a leap year, that would be perfectly valid...

        Whilst the order maybe unspecified and potentially vary from build to build, I would expect that the same line of code to evaluate in a consistant fashion within a given build, especially within a given run.

        Unless of course it is littered with :)

        /* Teach them not to bracket enough {evil grin} */ switch ( int( rand()*varients ) ) { case 0: {...}; case 1: {...}; ....; }

        Examine what is said, not who speaks.
        "Efficiency is intelligent laziness." -David Dunham
        "When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller
        If I understand your problem, I can solve it! Of course, the same can be said for you.

Re: Re: An obscure side effect?
by dws (Chancellor) on Aug 03, 2003 at 19:44 UTC
    I think you may simply be running into a case where Perl's order of operations is undefined.

    Good guess, but a simple experiment disproves it. You can put parentheses around the substr's in the original example to force the order of evaluation.

      Perhaps there's some way to do this, but in general parentheses control the order of evaluation for math operators, not the order of execution for assignments or argument evaluation. For example, how would you parenthesize to make
      $a[$i++] = $i++;
      evaluate the left-hand side before the right, producing $a[0] = 1? Or to make
      $j = $i++ - $i++;
      evaluate the second $i before the first, producing 1?

      How would you suggest parenthesizing in swab to guarantee the order of execution for this?

        [I]n general parentheses control the order of evaluation for math operators, not the order of execution for assignments...

        Unless assignment is just another way to build expressions. See perlop, and note that the assignment operator has associativity and precedence.

        Postincrement/postdecrement are tricky beasts. In C (at least in K&R C), their timing is left to the whim of the implementer. The swab() example involves a side-effect (substr() as lvalue), but the timing of the side-effect is well-defined.