I have to deep copy a data structure, which consists of scalars, hashes, and coderefs, but have to do some processing when I get to the "coderef" part, i.e. this is not copied from the original structure but replaced with other data. There won't be any circular references, so my code doesn't have to deal with that.

I am aware that there are several CPAN modules that can deep copy data. Storable even seems to have hooks that can influence how data is being copied, but I'm not sure I understand how to use that to my advantage. Of course I could simply copy the structure with a CPAN module function, then traverse it, remove the coderefs and replace them with the data I want. Seems clumsy.

Merlyn has a nice article on Deep Copy and Recursion on his site. The following is an adaptation of code that I used shamelessly:
sub _copy { my $this = shift; if (not ref $this) { $this } elsif (ref $this eq "HASH") { +{map { $_ => _copy($this->{$_})} keys %$this} } elsif (ref $this eq "ARRAY") { [map _copy($_), @$this] } elsif (ref $this eq "CODE") { # do some processing here, the following is a # placeholder 'CODEREF' } else { Carp::croak "What's a " . ref $_ . "?" } }
This works quite well. But let us assume, for the sake of the argument, that this should be implementet iteratively, not recursively.
I came up with this code, which takes a hashref as an argument and returns a copy of a data structure consisting of (references to) hashes and scalars, and inserts some placeholder text in place of coderefs.
sub _iterative_copy { my $this = shift; my @pile = $this; my $this_new = {}; my $root = $this_new; my @pile_new = $this_new; while (defined($this = shift @pile)) { $this_new = shift @pile_new; if (ref $this eq "HASH") { for (keys %$this) { if (ref (my $v = $this->{$_})) { if (ref $v eq "HASH") { $this_new->{$_} = {} } elsif (ref $v eq "ARRAY") { $this_new->{$_} = [] } elsif (ref $v eq "CODE") { # do some processing here, # the following is a placeholder $this_new->{$_} = "CODEREF" } else { Carp::croak "What's a " . ref $v } unshift @pile, $v; unshift @pile_new, $this_new->{$_}; } else { $this_new->{$_} = $v } } } # quietly ignore coderefs, # they are dealt with in the inner loop elsif (ref $this ne "CODE") { Carp::croak "What's a " . ref $this } } $root;
It doesn't look very elegant. Can this be implemented more efficiently? I ask for enlightenment, for the purpose of understanding how to implement this sort of thing iteratively. One can easily imagine where using recursion on a very large data structure would use up a lot of memory.

Thank you.

In reply to Iteratively deep copying a data structure by telcontar

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.