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

I have come across what appears to be a bug, although I'm sure there must be some reason this is happening. I am looping on an array reference, storing each value in a my-scoped variable, then modifying this variable. After the loop, the original array retains the changes made to the local variable!

Test case:
use Data::Dumper; my $ref = []; push @{$ref}, qw(abcd efgh ijkl); print Dumper($ref); foreach my $test (@$ref) { $test = substr($test, 0, 2); } print Dumper($ref);
Output:
$VAR1 = [ 'abcd', 'efgh', 'ijkl' ]; $VAR1 = [ 'ab', 'ef', 'ij' ];
If this is intended behaviour for array references, please let me know. :)

Replies are listed 'Best First'.
Re: Strange array reference behaviour (bug?)
by broquaint (Abbot) on Aug 12, 2003 at 15:47 UTC
    This is behaviour is due to the fact that foreach loops alias their topic to the current value, as opposed to copying it. So when you modify $test you're changing the actual value that you're iterating over. This can be confusing, as you've noted, but it can also prove to be very handy e.g
    my @strs = (' this', ' is', ' a ', ' list '); s/^\s+|\s+$//g for @strs; print "@strs\n"; __output__ this is a list
    See. perlsyn for more info.
    HTH

    _________
    broquaint

Re: Strange array reference behaviour (bug?)
by Zaxo (Archbishop) on Aug 12, 2003 at 15:50 UTC

    That is the intended behavior. It allows a reference to be passed to a sub which may alter the content, just as may be done with pointers in C.

    Your loop alters @$ref because you tell it to. $test is an alias to each element in turn.

    You can force copying of the array with,

    for (my @foo = @$aref) { #... }
    or,
    for (@{[@$aref]}) { #... }

    After Compline,
    Zaxo

      Or, just don't modify the topic. I can't think of an instance where you can't do something other than modify the topic. Sounds like your initial design could do with some refactoring. Good luck!

      ------
      We are the carpenters and bricklayers of the Information Age.

      The idea is a little like C++ templates, except not quite so brain-meltingly complicated. -- TheDamian, Exegesis 6

      Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

      Thanks, this is what I ended up doing, copying the reference to a temporary array. This is still really confusing...I would expect since I was using a my variable it would never alter the array. I would at least expect there to be a warning message printed in this case, but no warning is generated.

      I wonder if it's more expensive to have to create a temporary array rather than have foreach return real scalars for each value. Hmm...
Re: Strange array reference behaviour (bug?)
by edan (Curate) on Aug 12, 2003 at 15:49 UTC

    This is a documented feature of foreach. You can read all about it at the perlsyn page, under the Foreach Loops heading. This basically sums it up:

    That's because the foreach loop index variable is an implicit alias for each item in the list that you're looping over.

    So modifying it will change the element in the list, see?

    --
    3dan
Re: Strange array reference behaviour (bug?)
by sgifford (Prior) on Aug 12, 2003 at 18:27 UTC
    The easiest way to work around this feature is to just use a variable other than $test inside your loop:
    foreach my $test (@$ref) { my $test2 = substr($test, 0, 2); }