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

Dear Monks,

In my program I use arrays, array references, hashes and hash references
Now, when I pass one of those to a sub or module, they change the original array/hash (ref). Somehow I would like this behavious to change. So, how do I create a copy of one of the types described above ?, I guess this is not the best solution:
for(0..$X) { $newArray[$_] = $orig->[$_] ; }


Thanks a lot in advance
Luca

Replies are listed 'Best First'.
Re: Copying an array
by jhourcle (Prior) on Dec 05, 2005 at 13:56 UTC

    Depending on what you need to do, you can either call 'local' on the individual values, or you can just copy the whole array in one pass, so long as you don't have references in the array (or hash). You can also pass by value, rather than by reference, but only if you're passing a single array or hash (and again, it won't work w/ complex structures)

    sub whatever { my $array_ref = shift; my @copy = ( @$array_ref ); @copy[1] = 'whatever'; } sub blah { my $array_ref = shift; local $array_ref->[0] = 'blah'; } sub test { my @array = @_; $array[2] = 'test'; } my $test = [ qw( 0 1 2 3 4 5 ) ]; whatever($test); blah($test); test($test); use Data::Dumper; print Dumper $test;
Re: Copying an array or hash
by davidrw (Prior) on Dec 05, 2005 at 13:58 UTC
    my @newArray = @$orig;
    This, of course, has the caveat that it copies the contents, so if it's a huge array that might be bad memory-wise...

    Other caveat is that is the elements themselves are hash/array/object references, this isn't what you what -- you'll need a deep copy. I haven't used one, but you should be able to find something in Super Search or possibly one of Clone, Clone::PP, or Clone::Any would fit your needs..
      I haven't done deep copying either, but I've read about it. Merlyn has a good column at Deep Copy and Recursion, which has a simple deep copy routine that should work for most situations, and suggests using Storable::dclone for particularly hairy deep copying.
Re: Copying an array or hash
by psychotic (Beadle) on Dec 05, 2005 at 14:20 UTC
    This crops up often enough. Like mentioned, you shouldn't operate on $_ because it is an alias to the elements of the original data structure, and as such modifies them. Here is what cou can do:
    my $original = [qw(alpha beta gamma)]; my @duplicate; for (0..$#$original) { my $temp = $original->[$_]; $temp = rand(); push @duplicate, $temp; } print "Original: $_\n" for (@$original); print "Mofified: $_\n" for (@duplicate);
    Which gives the expected, and desired:
    Original: alpha Original: beta Original: gamma Mofified: 0.421875 Mofified: 0.108734130859375 Mofified: 0.69537353515625
    Update: Just to make things a bit more clear. I can obviously see that the original question is conserned with subroutine arguments being references. I merely expanded on the code that was attached as to not complicate the situation. Bull-blown examples with subroutines had already been posted prior to my reply. So fast to --?

    Update: you might be also interested in this node, global $_ behavior which discusses up to some extent aliasing.

Re: Copying an array or hash
by dorward (Curate) on Dec 05, 2005 at 14:03 UTC

    If you pass an array or a hash, then modifying it should not modify the original.

    my @foo = qw(x y z); bar(@foo)

    Update: Boy, do I feel like a fool.

    Of course, if you pass as a reference...

    my @foo = qw(x y z); bar(\@foo)

    ...or if you are dealing with a reference in the first place, then the original will be modified. To avoid this, you can dereference the array (or hash) within the subroutine. How you do that is described in perldoc perlref

      If you pass an array or a hash, then modifying it should not modify the original.
      use YAML qw( Dump ); sub yarly { $_[0] = "O RLY?" } my @a = ( "b", "c", "d" ); print Dump( \@a ), "\n"; yarly( @a ); print Dump( \@a ), "\n"; __END__ --- #YAML:1.0 - b - c - d --- #YAML:1.0 - O RLY? - c - d

      Elements of @_ are aliases for the originals.

Re: Copying an array or hash
by Moron (Curate) on Dec 05, 2005 at 16:00 UTC
    # a) make local copy of an array passed directly as parameters Fred( @orig ); sub Fred { # @_ is immediately a local copy of the original } # b) ... of an array passed by reference: Fred( \@orig ); sub Fred { my $aref = shift; my @copy = @$aref; #... } # c) ... of a hash passed directly Fred ( %orig ); sub Fred { my %copy = @_; #... } # d) ... and of a hash passed by reference: Fred( \%orig ); sub Fred { my $href = shift; my %copy = %$href; #... }

    -M

    Free your mind

Re: Copying an array or hash
by bageler (Hermit) on Dec 05, 2005 at 19:13 UTC
    Here's the deep copy sub that I use in some of my objects:
    sub deep_copy { my $self = shift; my $this = shift; unless (ref $this) { $this; } elsif (ref $this eq 'ARRAY') { [map $self->deep_copy($_), @$this]; } elsif (ref $this eq 'HASH') { +{map{$_ => $self->deep_copy($this->{$_})} keys %$this +}; } }
    Here's the sub that calls it:
    sub copy { my $self = shift; return $self->deep_copy($self); }
    of course, this works because I assume my object will only either have hashrefs, arrayrefs or scalars.