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

I am still struggling with the hash of hash business. I want to keep a record of randomly generated data in a hash so that it is kept with its population of origin. Here is what I have so far:
@populations = (4); &gener(); sub gener { foreach ($populations) { @chroma = (0...10); @chromb = (0...10); @ind = (0...10); %HoH = ( @populations => { individual => "@ind", chromasome1 => "@chroma", chromosome2 => "@chromb", }, ); } for $population ( sort keys %HoH ) { for $info ( sort keys %{ $HoH{$population} } ) { print "$population: $info=$HoH{$population}{$info} "; + } print "\n"; } use Data::Dumper; print Dumper(%HoH); }
But for the life of me I cant get it to work properly! It only populates the hash with #4. I want 4 hashes each with its own data (@chroma, @chromb, @ind will be created for each @population). Therefore I throw myself on the mercy of the wise monks!

Once this is working I also need a way to reference @chroma, @chromb, @ind for each population to modify them.

Thanks for your help!

Replies are listed 'Best First'.
Re: Hash of Hash Redux
by moritz (Cardinal) on Oct 31, 2007 at 14:50 UTC
    Though shalt use strict; and use warnings; - they would have catched a few of your errors.

    For example you have an array @populations, and a scalar $populations, but you treat the latter as if it was an array.

    Then you use a an array as a hash key - don't do that, hash keys are always stringified.

    Instead of individual   => "@ind", you probably want to write individual => \@ind,

    I can't provide you with a working sample because I didn't understand entirely what you want to do.

Re: Hash of Hash Redux
by johngg (Canon) on Oct 31, 2007 at 15:17 UTC
    Perhaps your array @populations should be a scalar count of however many populations you wish to generate. In your subroutine you can just create your category arrays once and interpolate them into new anonymous arrays for each population. If you take a reference to them as suggested by moritz then each population will have the same individual, chromosome1 and chromosome2 arrays.

    use strict; use warnings; use Data::Dumper; my %HoH; my $popCt = 4; gener(); print Data::Dumper->Dumpxs([\%HoH], [q{*HoH}]); sub gener { my @chroma = ( 0...10 ); my @chromb = ( 0...10 ); my @ind = ( 0...10 ); for my $pop ( 1 .. $popCt ) { $HoH{$pop} = { individual => [ @ind ], chromasome1 => [ @chroma ], chromasome2 => [ @chromb ], }; } }

    This produces

    I hope this is of use.

    Cheers,

    JohnGG

      That works like a charm. Thanks very much for the help!

      I have another thing...of course...the life of a noobie

      I want to now make @ind, @chroma @chromb interactive for each population

      Again this should work, but I am lost again. The @chroma, @chromb, @ind will then go into the hash maker that you all got working for me

      Also you all can yell at me all you want about use strict...no perl books have told me how to do that! I am flying without a net! Any good references?

        I am not sure what you are trying to do in terms of the algorithm but there are a few problems with your code.

        Firstly, you do push(my @chroma, $genea); and push(my @chromb, $geneb); inside the scope of the while ($ranpop) { ... } loop. That means that each time around the loop you create new lexically scoped arrays to push data onto and by the time you get to the return they are out of scope so inaccessible.

        Secondly, I'm not sure what you are doing with the

        my $center = 1; ... while ($center--) { ... return ... ; }

        That loop is meaningless as it stands because it is going to terminate early with the return and would only run the once anyway. If your $center is more than 1 it will still do the same thing as the return will always leave the subroutine. Perhaps the return should me moved to after the loop.

        Thirdly, if you want to return more than one array (or hashes for that matter) from a subroutine you must return them by reference because from the caller's point of view, the three arrays you return come back as one big list. Do it something like this.

        sub makeArrays { my @arrA = ( 1 .. 6 ); my @arrB = qw{ fred joe bill pete }; my @arrC = ( 1, 3, q{abc}, 8 ); return \@arrA, \@arrB, \@arrC; } my ( $refToArrA, $refToArrB, $refToArrC ) = makeArrays(); my @newArrA = @$refToArrA; my $elemTwoOfArrB = $refToArrB->[2];

        I hope this is of use.

        Cheers,

        JohnGG

        Strictures (strict and warnings) turn on various extra checking for dubious practices or programming errors. use strict requires (among many other things) that variables are declared (using my) and that various techniques that often cause confusion or have been made redundant in newer versions of Perl are not used.

        use warnings makes various run time checks. The most important one is that variables have been initialized before their contents are used. Together these two strictures provide an extremely useful early warning system for many common coding mistakes. Always use strictures in Perl. ;)


        Perl is environmentally friendly - it saves trees
Re: Hash of Hash Redux
by shmem (Chancellor) on Oct 31, 2007 at 15:10 UTC
    It only populates the hash with #4.

    That's because you do

    @populations = (4);

    Now you what do you have in @populations? Exactly one element, the 4. You iterate over that, and the loop is executed once setting $_ to 4.

    Inside your loop, you do

    %HoH = ( @populations => { individual => "@ind", chromasome1 => "@chroma", chromosome2 => "@chromb", }, );

    which resets %HoH at each pass. Well, it would, if you had more than one element in @populations.

    I guess you mean something like

    use strict; use warnings; use Data::Dumper; @populations = qw(mice cats dogs potatoes); my %hash = gener(@populations); sub gener { my @populations = @_; my %HoH; for (@populations) { my @chroma = (0...10); my @chromb = (0...10); my @ind = (0...10); $HoH{$_} = { individual => \@ind, chromasome1 => \@chroma, chromosome2 => \@chromb, }; } foreach my $population ( sort keys %HoH ) { + foreach my $info ( sort keys %{ $HoH{$population} } ) { + print "$population: $info = ", join(', ', @{$HoH{$population}{$info}}),"\n"; } } print Dumper(%HoH); return %HoH; # returned as list; to returna ref, use \%HoH }

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}