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

Hi!

I found out - :-( - that if you write a code like:

my @a = (1 .. 9); # for instance!! for my $e (@a) { $e += 1; }

you REALLY!! change the array element represented by $e.

So, to modify a copy you have to write:

my @a = (1 .. 9); # for instance!! for my $e (@a) { my $ee = $e # copy current element $ee += 1; }

Can anyone tell me if exists a shorter way or quicker solution to obtain what I want (i.e. to modify an array element) ?

Replies are listed 'Best First'.
Re: Modify array elements inside for loops
by MrNobo1024 (Hermit) on Mar 06, 2001 at 21:23 UTC
    my @a = (1 .. 9); # for instance!! for my $e (@{[@a]}) { $e += 1; }
    @{[@array]} makes an anonymous copy so you won't clobber the elements in the original array.
Re: Modify array elements inside for loops
by danger (Priest) on Mar 06, 2001 at 21:41 UTC

    Now, your post seems a little ambiguous (to my eyes), but if you are looking for a shorter way to directly modify array elements (not copies), then:

    my @array = (1,2,3); $_ *= 2 for @array; print "@array\n"; # prints: 2 4 6

    This works because the loop variable is aliased to each lvalue in the list in turn. This also means that such code will throw an exception if any of the elements in the list are not lvalues and you try to modify them:

    $_ *= 2 for 1,2,3; # error: modification of read-only value

    Now, if you actually meant you want to muck about without changing the actual array itself, then copying the loop variable inside the loop is one easy way. Another possibility is to generate the list via map():

    my @array = (1,2,3); for my $e (map $_, @array) { $e++; # or whatever } print "@array\n";

    However, this builds a new list and iterates over it, so it would be more efficient (memory-wise) to just make a copy of each item inside the loop as you did (at least for largish arrays).

Re: Modify array elements inside for loops
by mirod (Canon) on Mar 06, 2001 at 21:27 UTC

    This looks a little more natural:

    my @a = (1 .. 9); # for instance!! for my $e (@a) { my $ee = $e+1; # copy current element }

    In any case it's a trade-off. The current way allows you to modify the array in-place while still allowing you to easily, I like split infinitives BTW, use the elements without changing the array. If $e was just a copy it would be much harder to modify the array in-place. That's a good deal I think.

Re: Modify array elements inside for loops
by arturo (Vicar) on Mar 06, 2001 at 21:34 UTC
    my @new_a = map { $_+1 } @a;
    (faster? maybe not, but cooler and shorter, yes =)

    Whether or not this is what you want depends on what you are planning to do with the modified values. You could always do:

    foreach ( map { $_ +1 } @a ) { #whatever }

    As well. HTH

    Philosophy can be made out of anything. Or less -- Jerry A. Fodor

      Hmm. After experimenting (albeit with 5.5.3, the most recent usable release), I have seen that:
      foreach (map $_, @a) { $_++; # then whatever }
      is sufficient. Cool, I'll add that to my trick bag. Much easier than that crazy nested @{[@a]} I've seen so often (and think I actually invented from first principles at one point).

      -- Randal L. Schwartz, Perl hacker

Re: Modify array elements inside for loops
by arhuman (Vicar) on Mar 06, 2001 at 21:17 UTC
    use local :
    local @a = (1 .. 9); # How can I use my here { for my $e (local @a) { $e += 1; } }

    UPDATE : This is BAD !
    See below why and how to do it properly...
       
      local @a = (1 .. 9);  # How can I use my here 
      { 
         for my $e (local @a) { 
                $e += 1; 
         } 
      } 
      
      Your inner local @a there creates a new @a with an empty list. Add a print $e inside to see that.

      See the other answers in this thread for more correct answers.

      -- Randal L. Schwartz, Perl hacker

        Oops ! You're right I feel so ashamed...
        :-(

        of course
        for my $e (local @a=@a) {
        is too ugly to be used, mirod did it the right way anyway...

        UPDATE : Mirod, arturo, and you (merlyn) did it the right wayS (TIMTOWTDI)
        I still wonder how I managed to get a so poor solution with so many good ways laying in front of me.
        (Who said half-brained ?!?)