Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

Preserve array contents in for() loop

by dda (Friar)
on Sep 21, 2004 at 12:28 UTC ( [id://392613]=perlquestion: print w/replies, xml ) Need Help??

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

Hi Monks!

The following code changes contents of array @a:

use strict; use Data::Dumper; my @a = qw(1 2 3); print Dumper(\@a); for my $b (@a) { $b *= 2; } print Dumper(\@a);
Which is the best (most effective) way to make sure that no code inside the loop will modify array contents? I'm looking for a way to make the list not to be assignable.

--dda

Replies are listed 'Best First'.
Re: Preserve array contents in for() loop
by dragonchild (Archbishop) on Sep 21, 2004 at 12:31 UTC
    for my $b (my@c = @a) {

    In other words, make a copy. However, if you want complete safety, you will need to make a deep copy, not the shallow copy in my example. Storable has a dclone() method, which may be of use.

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

    Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

    I shouldn't have to say this, but any code, unless otherwise stated, is untested

      Thanks for the answer, though I had removed the similar code from my question before posting it. :) Could you please elaborate on 'deep copy vs. shallow copy'?

      --dda

        Let's say you have an array of hashes. Well, that's really an array of hash references. If you do foreach my $x (my @b = @a), what you're doing is making a copy of the hash references. So, $x = 3; won't affect @a, but $x->{foo} = 3; will affect $a[2]{foo}, if $x was aliased to the third element in @a, for instance.

        A deep copy will make a copy of both @a and everything that each element in @a might have, should they be references themselves to stuff, and if that stuff is a reference, etc.

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

        Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

        I shouldn't have to say this, but any code, unless otherwise stated, is untested

Re: Preserve array contents in for() loop
by Roy Johnson (Monsignor) on Sep 21, 2004 at 14:11 UTC
    I prefer making copies as they are needed, rather than all at once:
    for (@a) { my $b = $_; # Or do a deep copy, if necessary $b *= 2; }
    For making a temporary array, I like this idiom, though there's no performance reason to prefer it to any other:
    for my $b (map $_, @a) { $b *= 2; }
    others prefer
    for my $b (@{[@a]}) {

    Caution: Contents may have been coded under pressure.
Re: Preserve array contents in for() loop
by punkish (Priest) on Sep 21, 2004 at 14:48 UTC
    Let's try and understand why you are experiencing what you are.

    You are iterating over an array

    for my $b (@a) { ... }

    and actually changing its elements

    $b *= 2;

    So, obviously @a will change. You getting exactly what you are doing.

    Others have explained that you can make a copy of @a and then iterate over the copy. That way @a will not change.

    However, why don't you explain what exactly you want to achieve so we can help you better. The above snippet you have provided shows no syntactical errors, just perhaps an unintentional logical error. A list (an array) is just a variable, so by definition, it contains varying values. In most cases, you want this to be assignable. If you don't want it to change, just don't do anything to it... do things with it instead.

    By the way, you don't need to pass a ref to Dumper. It can take just about any variable, so just give it the array.

      The first part of your advice is rock solid: If you don't want to modify the content of the element of the array you're iterating over, knowing that the iterant is an alias to that element, don't modify it. It's kind of like when the patient goes to the doctor and says, "It hurts when I press here.", and the doctor replies, "Don't press there." This is simply what aliasing means, and covered in perlsyn.

      However, you're wrong about just giving an array to Data::Dumper. Run the following snippet:

      use strict; use warnings; use Data::Dumper; my @array = qw/This That Other/; print Dumper @array; print Dumper \@array;

      The POD for Data::Dumper has the following two important statements:

      Given a list of scalars or reference variables, writes out their contents in perl syntax.

      ....And later on...

      Due to limitations of Perl subroutine call semantics, you cannot pass an array or hash. Prepend it with a \ to pass its reference instead. This will be remedied in time, now that Perl has subroutine prototypes. For now, you need to use the extended usage form, and prepend the name with a * to output it as a hash or array.

      ++ to your post though, aliases do modify the element they're aliased to, if you modify the value of the alias. In fact, if the list being iterated over is a literal list (not an array variable), you can't even modify the alias's value. So if the intent is to preserve the original content of the array, just don't go modifying the alias's value.


      Dave

        Thanks Dave. I am learning more and more that all possible Perl questions have already been answered... one just has to read the docs.

        That said, I guess I use Dumper so much that I didn't even notice what it was saying to me...

        #!/usr/bin/perl # test.pl use strict; use Data::Dumper; my @foo = ('a', 'b', 'c');

        When I run the above, I get...

        > test.pl $VAR1 = 'a'; $VAR2 = 'b'; $VAR3 = 'c';
        I didn't even notice that it was reporting each variable separately and not as a part of an array.

        I go looking for trees, and forget that I am in a forest.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others avoiding work at the Monastery: (3)
As of 2024-04-19 01:29 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found