http://qs321.pair.com?node_id=285374


in reply to array confusion

In loops like this, Perl does some magic that other popular programming languages generally do not. For folks who come from other languages or are not familiar with this magic, it can be surprising, and often in negative ways.

You might normally think that the value of each element in the list is copied to the loop variable ($item in this case). In fact, the loop variable becomes a synonym for the elements of the list. This means that if you modify the loop variable, it will modify the object inside the list to which it is currently referencing.

To be even more specific, this only applies when the list element currently being processed is an "lvalue" (fancy language for "something that can be modified"). So, if the code is

foreach my $item ($a, 2, $c) { $item = 1; }
then $a and $c will be set to 1, but the constant "2" will be copied into $item, $item will be changed to 1 and the constant "2" will be left alone.

This is why your first code sample works the way that you want it to. You have listed the actual variables you want to modify inside the foreach loop.

In your second sample, you are first copying the values in your variables into a new array and using that array in the loop. Now, you're modifying the elements of the array inside the loop and not the original variables.

Another place where this type of synonym behavior works is in calls to subroutines. The elements of @_ are actually referencing the variables of the caller's parameters (where they are lvalues). Most of the time we tend to copy @_ elements into local variables; modifying those will not modify the caller's data.

But I ramble.

-- Eric Hammond

Replies are listed 'Best First'.
Re: Re: array confusion
by jonnyfolk (Vicar) on Aug 21, 2003 at 07:31 UTC
    Following on from the foregoing to achieve a result from the second example the same as the first you could do:
    my @look_for = ($name,$nerd,$noodle,$froodle,); foreach my $item (@look_for) { $item =~ s/$i_seek/ /g; } ($name,$nerd,$noodle,$froodle,) = @look_for;

    But this looks a little clumsy - maybe there's a better way?

      That is one way. It assigns the list of values contained in @look_for back to the original scalar variables.

      Another way is using references.

      # Assign to @look_for, references to $name, etc. # Note: \($x, $y, $z) is the same as (\$x, \$y, \$z) # because the reference symbol binds to each element in # the list. my @look_for = \( $name, $nerd, $noodle, $froodle,); foreach my $item ( @look_for ) { # notice how I dereference $item so that we get # to what it points to. $$item =~ s/$i_seek/ /g; }

      That will do what he's looking for, unless, of course, we've now broken other uses of @look_for in his code where his code might be expecting @look_for to contain scalar values rather than references to scalar variables.

      Dave

      "If I had my life to do over again, I'd be a plumber." -- Albert Einstein

Re: Re: array confusion
by Anonymous Monk on Aug 21, 2003 at 06:48 UTC
    And the flickering light finally becomes established. Thanks for that clear and erudite explanation.