Clean way to copy a Perl hash through Perl 5 API


I have a variable HV *myHash - which may be a simple Perl hash, an object or a tied variable. I want to make a copy of it (its variables) into a new variable myHash2, and destroy it afterwards.  

I just need two functions that are proven to work fine (and are very safe) for copying/deleting. Clean/short solutions would really be appreciated.
kushcuAsked:
Who is Participating?
 
terageekCommented:
To deep copy tied and/or blessed values it is a bit complex.  The code below should also copy duplicate references as duplicate references and circular references as circular references without going into an infinite loop.

my %copied_refs;
sub deep_copy {
    my @ret_val = &_deep_copy(@_);
    undef %copied_refs;
    return @ret_val;
}

sub _deep_copy {
    my @copyAr;
    foreach (@_) {
        my $copy;
        if (blessed $_) {
            $copy = &_deep_copy(blessed $_);
            bless $copy, ref blessed $_;
        } elsif (tied $_) {
            tie $copy, ref tied $_;
            if ((tied $_)->isa("HASH")) {
                %{tied $copy} = %{&_deep_copy(tied $_)};
            } elsif ((tied $_[0])->isa("ARRAY")) {
                @{tied $copy} = @{&_deep_copy(tied $_)};
            } else {
                ${tied $copy} = ${&_deep_copy(tied $_)};
            }
        } elsif (ref $_) {
            if (exists $copied_refs{$_}) {
              # For circular and duplicate references, return a
              # pointer to the previously made copy.  For cirular
              # references this avoids an infinite loop, and
              # creates a circular reference.  For duplicate
              # references, this will create a duplicate reference
                push @copyAr, ${$copied_refs{$_}};
                next;
            } else {
              # If this is a reference, save it and a pointer
              # to the copy so circular references and duplicate
              # references can be copied accurately
                $copied_refs{$_} = \$copy;
            }
       
            if (ref($_) eq "HASH") {
                while (my ($key, $value) = each %$_) {
                    $_->{$key} = &_deep_copy($value);
                }
                foreach
            } elsif (ref($_)eq "ARRAY") {
                $copy = [&_deep_copy(@$_)];
            } elsif (ref($_)eq "SCALAR") {
                $copy = &_deep_copy($$_);
                $copy = \$copy;
            } else {
                $copy = $_;
            }
        } else {
            $copy = $_;
        }
        push @copyAr, $copy;
    }
    return @copyAr;
}

Deep delete...

undef *myHash;

Perl will handle doing a deep delete for you in most cases.  The only time perl has an issue with garbage collection is when you have circular references.  If you have multiple pointers to the same object, Perl will also not delete the object, however you usually don't want it to be deleted in that case.  You can try to write a deep delete function to take care of circular references, but you won't be able to tell if you are messing up a second reference to one of the contained objects. For example...

%a = (a => 1);
%b = (b => \%a, c => {c => 2});

If you attempt to deep delete %b, you will destroy %a.  If you let perl do it, the hash { c => 2} will be deleted by perl, but %a won't be destroyed.
0
 
ozoCommented:
#A shallow copy of a hash can be done with
%newhash = %oldhash;
0
 
kushcuAuthor Commented:
yes, but I'm not in the Perl domain. My answer would probably include hv_iterinit and hv_iternext and some functions to fetch/delete data.
0
Get expert help—faster!

Need expert help—fast? Use the Help Bell for personalized assistance getting answers to your important questions.

 
kushcuAuthor Commented:
And I need a deep copy.
0
 
TintinCommented:
I must admit I wasn't aware of what shallow/deep copying was.

Perhaps the following article will be useful.

http://www.stonehenge.com/merlyn/UnixReview/col30.html
0
 
kushcuAuthor Commented:
I think my original posts weren't clear enough.

I'm writing this code in C (that's how I have HV*). I need C (or XS) code that uses Perl 5 API to do the copying. I need a deep copy of level 1, so non-recursive solutions would win over recursive ones.

Thanks.
0
 
kushcuAuthor Commented:
the XS code for Clon does what I want. didn't want to keep the question open.
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.