http://qs321.pair.com?node_id=356361

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

I want to have an inherited datastructure in a configuration module I write. There is a bavarian, who shall be a german, who shall be a european.

First she will be a european with all attributes, then the german attributes will be added. Attributes which do not override the european attributes stay. Now the bavarian attributes which override the ones being in the structure.

This looks like deep copy where I found a long thread. But this seems not to be a problem of creating a new data structure (module Clone, Storage::dclone), but to build an existing up and up. I wrote this code to demonstrate:

#!/usr/bin/perl use strict; use Data::Dumper; my %europe = ( skin => "euro-white", language => { default => "euro-englisch", instrument => "euro-clarinet", }, politics => "euro-democratic", ); my %german = ( skin => "de-white", language => { default => "de-german" } ); my %bavarian = ( skin => "bav-white", language => { default => "bav-bavarian" } ); # this is not enough, german/language overwrites # the complete hash of euro/language my %woman = ( %europe, %german, %bavarian ); print Dumper(\%woman); # result is this: # $VAR1 = { # 'skin' => 'bav-white', # 'politics' => 'euro-democratic', # 'language' => { # 'default' => 'bav-bavarian' # ** this is not here: instrument => "euro-clarinet", # } # }; unless ($woman{language}->{instrument}) { die "Error: Language instrument is not there"; } else { print "It worked\n"; }
Help is very much appreciated! Thanks!

Replies are listed 'Best First'.
Re: deep copy, not deep create
by saintmike (Vicar) on May 25, 2004 at 20:46 UTC
    It seems to me that instead of manipulating data structures you should be using an object-oriented approach: A European class/package, from which the German class/package inherits its language() etc. methods.

    Inheritance will take care of your 'overriding' requirements.

    To mock around with these concepts, use Class::Prototyped:

    use Class::Prototyped; my $european = Class::Prototyped->new( language => 'English', food => 'Europizza', ); my $german = Class::Prototyped->new( 'parent*' => $european, food => 'Wiener Schnitzel', ); my $bavarian = Class::Prototyped->new( 'parent*' => $german, ); print "Default language: ", $bavarian->language(), "\n"; $bavarian->language("Bavarian"); print "Explicitely set language: ", $bavarian->language(), "\n"; print "Default food: ", $bavarian->food(), "\n";

      I've only just started using OOP, but isn't the issue of attribute storage structure completely orthogonal to whether the interface is procedural or object oriented? I mean, even if the data happens to be stored in an object, the programmer still has to worry about how it is stored, still has to bless *some* sort of data structure into object-dom, and if object attributes are stored internally as a hash, as is usually the case, this issue will still come up.

      You mention inheritence, but inheritence affects only how methods are invoked and which methods are invoked, right? Again, it would seem methods are orthogonal to data.

      Your response does sort-of address the issue by implying that the data structure should be flattened and any desired hierarchy managed by methods rather than the actual data structure, but you never come out and say that.

      You also talk about "an object oriented approach" and "manipulating data structures" as though they are two different things. Given that *someone* has to design the object oriented data representation in the first place, I wonder what you mean.

      Just curious. I honestly don't see how OOP applies here.

      Respectfully,
      RT

Re: deep copy, not deep create
by eclark (Scribe) on May 25, 2004 at 21:50 UTC

    I agree with saintmike. What you want to do is better using OO. To use the approach you are now, you can do something like this. See my deepmerge function. It is not complete, but works for your sample data. To complete it you need to handle all data types.

    #!/usr/bin/perl use strict; use Data::Dumper; my %europe = ( skin => "euro-white", language => { default => "euro-englisch", instrument => "euro-clarinet", }, politics => "euro-democratic", ); my %german = ( skin => "de-white", language => { default => "de-german" } ); my %bavarian = ( skin => "bav-white", language => { default => "bav-bavarian" } ); # this is not enough, german/language overwrites # the complete hash of euro/language my %woman = %europe; deepmerge(\%woman, \%german); deepmerge(\%woman, \%bavarian); print Dumper(\%woman); # result is this: # $VAR1 = { # 'skin' => 'bav-white', # 'politics' => 'euro-democratic', # 'language' => { # 'default' => 'bav-bavarian' # ** this is not here: instrument => "euro-clarinet", # } # }; unless ($woman{language}->{instrument}) { die "Error: Language instrument is not there"; } else { print "It worked\n"; } sub deepmerge { my ($into, $this) = @_; for my $k (keys %$this) { if (defined $k and exists $into->{$k} and defined $into->{$k} and ref($into->{$k}) eq 'HASH' and ref($this->{$k}) eq 'HASH') { deepmerge($into->{$k}, $this->{$k}); } elsif (defined $k) { $into->{$k} = $this->{$k}; } } }
Re: deep copy, not deep create
by dfaure (Chaplain) on May 26, 2004 at 08:18 UTC
Re: deep copy, not deep create
by bsb (Priest) on May 25, 2004 at 23:12 UTC
    I agree the Class::Prototyped maybe a better solution but you could also check Tie::Hash::Layered

    Otherwise:

    %bavarian = ( dclone(%european), dclone(%german) );