Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
PerlMonks  

Readonly references, replicating data structures

by nikmit (Sexton)
on Jan 22, 2017 at 11:37 UTC ( [id://1180111]=perlquestion: print w/replies, xml ) Need Help??

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

When I initially hit this it seemed incomprehensible... then I figured out why it's happening but not yet how to fix it - so here I am humbly asking for your wisdom.

I expected the below code to create a fresh copy of @arr every time gimme() is executed. Instead it creates a new reference for @arr but reuses all of the nested references. The result is that gimme() returns the last box with its content, rather than a fresh empty box which is what I want...

I considered using Readonly or a configuration file on disk to ensure I get an empty box every time, but both seem clunky and slow. The actual data structure is nested to 5-6 levels and while it is not huge, it will be executed often.

I suspect turning the data structure into an object may be the right path, but I'm yet to pick that side of perl up... What would be your advice?

#!/usr/bin/perl -w use strict; use Data::Dumper; BEGIN { my @arr = ( { box => { attr => { this => 'that', foo => 'bar', }, content => {}, }, }, ); sub gimme { my @result = @arr; return \@result; } } my $box1_ref = gimme(); $box1_ref->[0]->{white_box}->{content}->{apples} = 5; print "tracing: $box1_ref -> $box1_ref->[0] -> $box1_ref->[0]->{white_ +box} -> $box1_ref->[0]->{white_box}->{content}\n"; print Dumper $box1_ref->[0]->{white_box}->{content}; my $box2_ref = gimme(); print "tracing: $box2_ref -> $box2_ref->[0] -> $box2_ref->[0]->{white_ +box} -> $box2_ref->[0]->{white_box}->{content}\n"; print Dumper $box2_ref->[0]->{white_box}->{content};

Result of that code is:

tracing: ARRAY(0x20d9e70) -> HASH(0x1fcf638) -> HASH(0x20d9db0) -> HAS +H(0x20d9de0) $VAR1 = { 'apples' => 5 }; tracing: ARRAY(0x1fab0f0) -> HASH(0x1fcf638) -> HASH(0x20d9db0) -> HAS +H(0x20d9de0) $VAR1 = { 'apples' => 5 };

Replies are listed 'Best First'.
Re: Readonly references, replicating data structures
by haukex (Archbishop) on Jan 22, 2017 at 12:20 UTC

    Hi nikmit,

    I expected the below code to create a fresh copy of @arr every time gimme() is executed.

    In the code you showed, @arr is only initialized once, so the anonymous hashes are only created once. The first and only element of @arr is a reference to an anonymous hash. When you say my @result = @arr;, the elements of @arr are copied into @result, so that reference is copied exactly, so it still points to the same anonymous hash. This is known as a "shallow copy" and is how arrays and hashes are copied in Perl. (Note: Making @arr Readonly won't change that.)

    To get a "deep copy" or "clone", choroba already pointed you to Storable and Clone, but there's another solution:

    sub gimme { my @result = ( { box => { attr => 'foo' }, } ); return \@result; }

    This causes the array and the anonymous hashes to be re-created on every call to gimme.

    Hope this helps,
    -- Hauke D

    Update: Minor clarifications to wording.

Re: Readonly references, replicating data structures
by choroba (Cardinal) on Jan 22, 2017 at 12:00 UTC
    See Storable's dclone or Clone's clone for solutions.

    ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,
Re: Readonly references, replicating data structures
by kcott (Archbishop) on Jan 23, 2017 at 00:47 UTC

    G'day nikmit,

    I probably would have written that more like this:

    #!/usr/bin/env perl -l use strict; use warnings; use Data::Dump; for (1..3) { my $ref1 = gimme(); $ref1->[0]{box}{y}{z} = $_; $ref1->[0]{box}{w} = 'x' if $_ == 2; $ref1->[0]{box}{a}{d} = 4 if $_ == 3; trace($ref1); my $ref2 = gimme(); trace($ref2); } sub gimme { [ { box => { a => { b => 2, c => 3 }, y => {} } } ] } sub trace { my ($ref) = @_; print 'Tracing: '; print join ' -> ', $ref, $ref->[0], $ref->[0]{box}, $ref->[0]{box}{y}; dd $ref; return; }

    Notes:

    • The BEGIN block, as well as @arr, have gone.
    • &gimme uses no variables: it just returns an anonymous data structure that's created afresh every time it's called.
    • There are no multiple instances of \@result hanging around. That equates to no instances of @results in existence whose reference counts are greater than zero: they can't be garbage collected until their reference count reaches zero.
    • I threw in some additional modifications on the 2nd and 3rd iterations: no changes to $ref1 were propagated to $ref2.
    • Note that you don't have to use the '->' between every element of the data structure: just use it once, e.g. $ref1->[0]{box}{a}{d}.

    Here's the output from a sample run:

    Tracing: ARRAY(0x7fdc5307b7c8) -> HASH(0x7fdc5282e280) -> HASH(0x7fdc5282de48) +-> HASH(0x7fdc5282dd88) [{ box => { a => { b => 2, c => 3 }, y => { z => 1 } } }] Tracing: ARRAY(0x7fdc530b1f18) -> HASH(0x7fdc530b4bc0) -> HASH(0x7fdc530251a0) +-> HASH(0x7fdc53025170) [{ box => { a => { b => 2, c => 3 }, y => {} } }] Tracing: ARRAY(0x7fdc530b4bc0) -> HASH(0x7fdc52846220) -> HASH(0x7fdc5282def0) +-> HASH(0x7fdc5282dd70) [ { box => { a => { b => 2, c => 3 }, w => "x", y => { z => 2 } } }, ] Tracing: ARRAY(0x7fdc530b8a48) -> HASH(0x7fdc53025188) -> HASH(0x7fdc530b1ee8) +-> HASH(0x7fdc530b7968) [{ box => { a => { b => 2, c => 3 }, y => {} } }] Tracing: ARRAY(0x7fdc530b8a48) -> HASH(0x7fdc5282dda0) -> HASH(0x7fdc53094b50) +-> HASH(0x7fdc5282de48) [ { box => { a => { b => 2, c => 3, d => 4 }, y => { z => 3 } } }, ] Tracing: ARRAY(0x7fdc530b1f48) -> HASH(0x7fdc530b7a88) -> HASH(0x7fdc530aabb0) +-> HASH(0x7fdc530b1e58) [{ box => { a => { b => 2, c => 3 }, y => {} } }]

    — Ken

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others goofing around in the Monastery: (2)
As of 2024-04-26 07:51 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found