in reply to Re^4: How best to replicate/copy a hash of hashes
in thread How best to replicate/copy a hash of hashes
This node is confusing enough that I am attempting a kind of "mini-tut" re: cloning - might help somebody sometime. Probably redundant with other stuff, but perhaps with a bit of a different twist.
The reason that the first code benchmark ran so apparently fast was that it didn't actually make a full copy of the structure, it only copied references, essentially just the pointers to the original copies of sub-parts. This is known as a "shallow copy". A "deep copy" is different and is known as a "clone". A "clone" is an exact and independent copy of the original. Once a structure has been cloned, the original can be modified without affecting the clone. If only references are copied (as in a "shallow copy"), then changes to the original will also affect the "copy" because there is still a "connection" between the copy and the original via references.
Perl can make a clone with the "=" operator for any fundamental data type. $A = $B; means that $A is an exact and independent copy of $B, likewise @A = @B;, array A is an exact and now independent copy of B, %A = %B. However, that won't work with more complex data types because references are involved - if I copy a reference, that doesn't copy "the thing" just a "pointer to that thing".
In the code below: I show a structure. I clone it. Then I change the original and then show that the clone didn't change.
In Perl, the easiest "complex" structure to clone is the Array of Array. I show one called amazingly enough "@AoA". This is an array of references to arrays. To "clone" this, I use a map{}. Each reference to an array becomes $_ inside the map{}. Then that array reference is de-referenced via @$_ to become the complete array again. The square brackets mean to allocate new memory for that copy of the array and then a reference is returned for that memory to go to the left side.
I show a couple of lines that change the original and then print statements to show that the clone didn't change. Next up is the HoH, Hash of Hash.
This is more complicated because there isn't any fancy map{} trick that will do the job. But the "formula" is still the same as above. Each key in the HoH represents a reference to another hash. So, $HoH{$key} is a hash reference. That is de-referenced to get the complete hash, then the curly braces cause new memory to be allocated for that "completely copied" hash and a reference to that memory is set to the clone's corresponding hash key. Use curly braces to allocate new anon memory for a hash and use square brackets to allocate new anon memory for an array.
Cloning a particular sub-hash of a HoHoH is actually a bit easier. This can be de-referenced to a simple hash and Perl can copy that into an independent variable.
Maybe this is as clear as mud, but I tried. Code is below.
#!/usr/bin/perl -w use strict; use Data::Dumper; my @AoA = ( [1,2,3], [4,5,6], ); my @cloneAoA = map{[@$_]}@AoA; $AoA[0][1] = 99; $AoA[1]->[2] = 88; print Dumper \@AoA; print "the cloneAoA is still the same, but AoA changed!\n"; print Dumper \@cloneAoA; print "-------- now HoH ----------------\n"; my %HoH = ( 'a' => { 'x' => 1, 'y' => 2, }, 'b' => { 'xx' => 3, 'yy' => 4, }, ); my %cloneHoH; foreach my $key (keys (%HoH)) { $cloneHoH{$key} = { %{$HoH{$key}} }; } $HoH{'a'}{'x'} = 88; $HoH{'b'}{'yy'} = 99; print Dumper \%HoH; print "the clone is still the same, although %HoH changed!\n"; print Dumper \%cloneHoH; print "========= now HoHoH ==============\n"; my %HoHoH = ('x' => { 'ao' => { 'mx' => 1, 'my' => 2, 'mz' => 3, }, 'ap' => { 'nx' => 4, 'ny' => 5, }, }, 'y' => { 'bq' => { 'bx' => 6, 'by' => 7, }, }, ); #to clone the $HoHoH{'x'}{'ap'} sub-hash... my %xapClone = %{$HoHoH{'x'}{'ap'}}; $HoHoH{'x'}{'ap'}{'nx'} = 99; $HoHoH{'x'}{'ap'}{'ny'} = 88; print Dumper \%HoHoH; print "the %xapClone still has nx => 4 and ny => 5\n"; print Dumper \%xapClone; __END__ #output shown below.... in <readmore> section
$VAR1 = [ [ 1, 99, 3 ], [ 4, 5, 88 ] ]; the cloneAoA is still the same, but AoA changed! $VAR1 = [ [ 1, 2, 3 ], [ 4, 5, 6 ] ]; -------- now HoH ---------------- $VAR1 = { 'a' => { 'y' => 2, 'x' => 88 }, 'b' => { 'yy' => 99, 'xx' => 3 } }; the clone is still the same, although %HoH changed! $VAR1 = { 'a' => { 'y' => 2, 'x' => 1 }, 'b' => { 'yy' => 4, 'xx' => 3 } }; ========= now HoHoH ============== $VAR1 = { 'y' => { 'bq' => { 'bx' => 6, 'by' => 7 } }, 'x' => { 'ao' => { 'mx' => 1, 'mz' => 3, 'my' => 2 }, 'ap' => { 'ny' => 88, 'nx' => 99 } } }; the %xapClone still has nx => 4 and ny => 5 $VAR1 = { 'ny' => 5, 'nx' => 4 }; </readmore>
|
|---|