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

hi, I'm tieing HoH, with the following structure :
$hash{keylvl1}{keylvl2}
The shortened code is below..
package Tie::HashDF; use strict; BEGIN { require Exporter; require Tie::Hash; @Tie::HashDF::ISA = qw(Exporter Tie::StdHash); @Tie::HashDF::EXPORT = qw(); } sub tolog { print "$_[0] : $_[1]\n"} sub TIEHASH { my $self = bless {}, shift; return $self } sub FETCH { tolog 'FETCH', "$_[1] : $_[0]{$_[1]}"; $_[0]{$_[1]} } sub STORE { tolog 'STORE'; } 1;
When I do this several assignments I get FETCHE's instead of STORE:
#!/usr/bin/perl use strict; use lib '/work/lib'; use Tie::HashDF; my $obj = tie my %hash, 'Tie::HashDF'; #my $a = $hash{toby}{ip}; $hash{toby}{ip} = '111'; $hash{drago}{account} = 'halo'; #undef $obj; #untie %hash;
-----
FETCH : toby : HASH(0x821a9a0) FETCH : drago : HASH(0x81433fc)
Could this be problem 'cause it is not onlevel-hash.. When I write this hash to disk all changes I have made are seen. i.e. it works as expected from me, but instead of triggering STORE it triggers FETCH..
I want to be sure when I'm doing STORE so that I do some checks there.
If I uncomment the line with assignment of hash element to variable I get another FETCH i.e.
FETCH : toby : HASH(0x821a9dc) FETCH : toby : HASH(0x821a9dc) FETCH : drago : HASH(0x8143520)
what I'm doing wrong ?

Replies are listed 'Best First'.
Re: tieing HoH
by BrowserUk (Patriarch) on Jan 14, 2004 at 13:51 UTC

    When you execute the statement $hash{toby}{ip} = '111'; on a newly created (and tied) hash, you are asking perl to

    1. Create a key named 'toby' in the hash (named '%hash').
    2. Set the value associated with that key to a new, empty, untied anonymous hash.
    3. Create a new key named 'id' in that anonymous hash.
    4. Set the value associated with that key ('id') to the string '111'.

    The actual sequence perl performs thise steps is probably different, but they must all be performed.

    In other words, the assignment $hash{toby}{id} = '111'; isn't an assignment to the hash %hash;

    It is an assignment to the key 'id', within the anonymous hash that is the value of the key 'toby' within the hash %hash.

    As %hash is newly created and the key 'toby' does not exist, perl has to 'autovivify' (ie. create) the key 'toby' and assign an anonymous hash to it, before it can create the 'id' key and assign '111' to it.

    Not sure if that clarifies anything?

    The short answer to "Could this be problem 'cause it is not one-level-hash..", is Yes!.

    If you want to tie a multilevel hash, you would have to inspect the values STOREd into your base-level hash, and if they are hash references, tie these also. This is non-trivial to get right.

    Hope that help is some way:)


    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "Think for yourself!" - Abigail
    Timing (and a little luck) are everything!

      yep i get the picture now...
      only thing I forgot to mention is that keys "toby" and "ip" exists already..
      They are populated inside Tie::HashDF with other methods by default..I have cleared them from this example to be easier for reading
      Havent tested keys that doesnt exist, will check that..
Re: tieing HoH
by Abigail-II (Bishop) on Jan 14, 2004 at 13:49 UTC
    That's because you have tied the top level hash. In order for Perl to store a value into $hash {toby} {ip}, Perl first has the fetch $hash {toby}, and that's the FETCH you are seeing.

    What you should be doing is tie the top level hash to another class, where a FETCH first looks if there's already a value - if not, it creates a tied hash (of your original class) first.

    Abigail

Re: tieing HoH
by artist (Parson) on Jan 14, 2004 at 18:45 UTC
    Check out Tie::TwoLevelHash. I think it tries to achieve exactly what you want.

    use Tie::TwoLevelHash; use strict; use warnings; my $hash; my $file = "datafile"; tie (%hash, 'Tie::TwoLevelHash', $file, 'rw'); $hash{PEOPLE} = {YOU => "me"}; $hash{PEOPLE} = {WHO => "cares"}; $hash{PEOPLE} = {WHO => "works"}; $hash{ME} = { I => "See"}; __DATA__ ME I: See PEOPLE WHO: works YOU: me

    ===========================================
    Stumbleupon

Re: tieing HoH
by flyingmoose (Priest) on Jan 14, 2004 at 17:11 UTC
    If you are storing records like this, you might want to consider Data::Dumper or YAML, that way you can dodge the debugging question entirely and get back to doing fun stuff :)

    Honestly, I take short cuts too often, but when they work cleanly...hey...I use them. I find Data::Dumper is fairly more reliable than YAML, easier to remember for Perl hackers, and less thrown into indent-level chaos when hacked by hand. But if you want the extra feature of printable/readable records by non-Perl types, YAML is more suitable.

Re: tieing HoH
by delirium (Chaplain) on Jan 15, 2004 at 12:47 UTC
    By the way, if you wanted to do this with the Data::Dumper module, start with something like this:

    use Data::Dumper; my %sess_hist = (); sub load_history { %sess_hist = %{do($hist_file)} or die 'failed to load history'; } sub save_history { local $Data::Dumper::Indent = 1; open HF, '>', 'some_file.txt'; print HF Dumper \%sess_hist; close HF; }