http://www.perlmonks.org?node_id=1022911

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

I am reading the book "Intermediate Perl" and I am having trouble with the section call marshalling Data (page 78). The idea is to save a data structure with Data::Dumper->Dump and later recover that data structure in another program by executing the saved string with eval.

The example in the book creates two hashes that reference each other. It then dumps both of them to a string. My code below produces the exact string that the book says to expect. For the sake of demonstrating my problem, I create a second package (Other) and attemp to reconstruct the hashes from the string in that package. The resulting hashes contain the right data, but they do not reference each other. I demonstrate this by showing that two references which should be equal are equal in the main package, but not in the Other package.

use warnings; use Data::Dumper; package main{ our @data1 = qw(one won); our @data2 = qw(two too to); push @data2, \@data1; push @data1, \@data2; open my $FH, '>', \$marshall_data; print {$FH} Data::Dumper->Dump( [ \@data1, \@data2 ], [qw(*data1 *data2)] ); close $FH; print "NO " if \@data2 != $data1[2]; print "CIRCULAR DATA STRUCTURE in package main\n"; } package Other{ open my $FH, '<', \$main::marshall_data; my $string = do {local $/ = undef; <$FH>}; close $FH; eval $string; die "$@\n" if $@; print "NO " if \@data2 != $data1[2]; print "CIRCULAR DATA STRUCTURE in package other\n"; print \@data2, ' ', $data1[2], "\n\n"; }

What Am I missing? Could there be an error in the book? or perl?

Bill

Replies are listed 'Best First'.
Re: marshalling data
by tobyink (Canon) on Mar 12, 2013 at 07:14 UTC

    If you take a look at the $marshall_data variable, it contains this line:

    @data2 = @{$data1[2]};

    This makes @data2 into a shallow clone of @{$data1[2]} - i.e. they are now different arrays. :-(

    I can get it to work if I serialize to arrayrefs rather than arrays. (PS: it's not that much work to get it to run under strict too!)

    use v5.14; use strict; # redundant! use warnings; use Data::Dumper; package main { our @data1 = qw(one won); our @data2 = qw(two too to); push @data2, \@data1; push @data1, \@data2; open my $FH, '>', \(our $marshall_data); print {$FH} Data::Dumper->Dump( [ \@data1, \@data2 ], [qw($data1 $data2)] ); close $FH; print "NO " if \@data2 != $data1[2]; print "CIRCULAR DATA STRUCTURE in package main\n"; } package Other { open my $FH, '<', \$main::marshall_data; my $string = do {local $/ = undef; <$FH>}; close $FH; our (@data1, @data2); (*data1, *data2) = do { my ($data1, $data2); # declare eval $string; ($data1, $data2); }; die "$@\n" if $@; print "NO " if \@data2 != $data1[2]; print "CIRCULAR DATA STRUCTURE in package other\n"; print \@data2, ' ', $data1[2], "\n\n"; }
    package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow->new->name
      Your solution requires Other to know the variable names at compile time. I think the whole idea is to preserve the variable names as well as their values.
      Bill

        Why in earth would Other want to unmarshall some variables without knowing what they're called? If it didn't know what they were called, it couldn't very well make practical use of the data contained in them.

        package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow->new->name
Re: marshalling data
by thomas895 (Deacon) on Mar 12, 2013 at 03:36 UTC

    There's one big flaw: you don't declare packages using braces. Instead, it goes like:

    package Foo; sub method { ... }

    Notice the difference.
    Next, you've used warnings -- this is great, since many people tend to forget that -- but you should also try strict. Many other problems are quickly found that way.
    Also, you don't print to file handles like you did. Rather, it goes more like:

    print $FH Data::Dumper->dump(...);

    Try that, see if it helps.

    ~Thomas~ 
    "Excuse me for butting in, but I'm interrupt-driven..."

      Actually, both of the uses of braces are valid alternatives (one only for moderately modern versions of Perl) to the methods you proposed. You can write "package main; ..." or "package main { ... }" (as of some non-ancient Perl version) and you can do "print $FH ..." or "print {$FH} ..." (since at least Perl 3).

      - tye        

        Huh, interesting.
        Well I have to admit I've never seen it used before. Oh well, I guess I learn something new every day.

        ~Thomas~ 
        "Excuse me for butting in, but I'm interrupt-driven..."

      There's one big flaw: you don't declare packages using braces . Instead, it goes like:

      On an old perl -- it depends on your perl vrsion, see package

      Package Blocks were introduced in perl 5.12. They have the advantage of limiting the scope of lexical variables to the package. I did not use strict in this example because the the concept that I am trying to learn makes changes to the symbol table. The hashes and the the string $marshalling_data must be global variables. Explicitly specifing all the exceptions to strict would mask my intention.
      Bill

        Perl 5.14 actually. Perl 5.12 introduced the package Foo 1.23 syntax.

        package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow->new->name
Re: marshalling data
by Anonymous Monk on Mar 12, 2013 at 03:53 UTC

    What Am I missing?

    If you ask me, it depends on what you're trying to learn.

    Consider

    $ perl -MData::Dump -e " my@f=1..3;my@g=4..6;push@f,\@g;push@g,@f;dd\ +@f,\@g; " do { my $a = [1, 2, 3, [4, 5, 6, 1, 2, 3, 'fix']]; $a->[3][6] = $a->[3]; ($a, $a->[3]); }

    Do you notice 'fix'? Do you notice how the self-referential-ness is established in a second step?

    Employ Basic debugging checklist and compare that to your Dumper output

    @data1 = ( 'one', 'won', [ 'two', 'too', 'to', \@data1 ] ); @data2 = @{$data1[2]};

    How is it different from what you originally had? Different from my dd output?

    The order of operations isn't the same, @data2 is set to the values of some anonymous array, as opposed to a reference to @data2 being pushed onto @data1 -- the two references will never be the same

    Could there be an error in the book?

    I wouldn't know, but I don't quite see the lesson its trying to teach -- DD isn't the greatest serialization tool, and string-eval isn't the greatest way to Undumper / Data::Undump

    or perl?

    Well no, but you could argue it's a bug in Data::Dumper-s prefix/varname feature