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

I'm new to Perl, and have been doing some reading and going thru some scripts we use here at work, and came across something that neither my reference book (Core Perl, by Reuven M. Lerner) addresses, nor my co-workers know exactly what it seems to do.

$srciphash{$srcip}{$dstip} = 1;

This is nested inside of a while loop, and no other action within the loop acts on srciphash. The $srcip and $dstip scalars are assembled from array elements earlier in the loop. I'm confused because I'm not exactly sure what it's doing.

After the while loop is done, this loop is started:

foreach $srcip (keys(%srciphash))

and within this loop, another loop is nested:

foreach $dstip (keys(%{$srciphash{$srcip}}))

Can anybody explain to me exactly what those 3 lines are supposed to mean? I'm hoping that I'm not the only Perl newb who's having trouble get a handle on the concept of hashes.

update (broquaint): title change (was wondering what exactly these lines does)

Replies are listed 'Best First'.
Re: nested hashes and their usage e.g $foo{bar}{baz}
by pfaut (Priest) on Feb 26, 2003 at 16:54 UTC
    $srciphash{$srcip}{$dstip} = 1;

    This builds a nested data structure - a hash of hashes (or hash references). The outer hash contains a key for each unique $srcip (source IP address?) value. The value associated with this key is (a reference to) another hash which has a key for each unique $dstip (destination IP address?). I would guess this is trying to build a table of source/destination pairs. The value is always set to 1 so it's probably only being used for definedness.

    foreach $srcip (keys(%srciphash))

    This iterates the outer hash and executes the block for each key it finds.

    foreach $dstip (keys(%{$srciphash{$srcip}}))

    This iterates the inner hash for the currently selected outer hash. The block associated with this should be invoked once for each unique $srcip/$dstip pair.

    --- print map { my ($m)=1<<hex($_)&11?' ':''; $m.=substr('AHJPacehklnorstu',hex($_),1) } split //,'2fde0abe76c36c914586c';
Re: nested hashes and their usage e.g $foo{bar}{baz}
by broquaint (Abbot) on Feb 26, 2003 at 16:57 UTC
    foreach $srcip (keys(%srciphash))
    This iterates through the keys of the hash %srciphash where each key is assigned to $srcip
    foreach $dstip (keys(%{$srciphash{$srcip}}))
    This iterates through the hash stored in %srciphash associated with the the key $srcip, the cluster of curly braces is dereferencing the stored hash, then assigning the keys of that hash to $destip.
    $srciphash{$srcip}{$dstip} = 1;
    This assigns 1 to the value corresponding to the key $dstip which is within a hash stored in %srciphash corresponding to the key $srcip.

    Hopefully this code should clarify the above statements.

    my %srciphash = ( foo => { one => undef }, bar => { two => undef }, baz => { three => undef }, ); foreach my $srcip(keys %srciphash) { foreach my $dstip(keys %{ $srciphash{ $srcip } }) { print "$srciphash{$srcip}{$dstip} = 1\n"; } } __output__ $srciphash{foo}{one} = 1 $srciphash{baz}{three} = 1 $srciphash{bar}{two} = 1
    See. perldata and perlsyn for more info on data structures and looping syntax.
    HTH

    _________
    broquaint

Re: nested hashes and their usage e.g $foo{bar}{baz}
by jasonk (Parson) on Feb 26, 2003 at 16:54 UTC

    It appears that %srciphash is being used to associate source ips ($srcip) with their destinations ($dstip), so %srciphash is a hash which has the source ip as the key, and an anonymous hash as the value. The anonymous hash then contains the destination ip as the key, and '1' as the value. The first foreach loop iterates through the srcips (putting them into $srcip), and the second one iterates through all the destination ips associated with that source ip (putting them into $dstip). If you were to add print "$srcip talked to $dstip\n"; inside the last loop, it would show the relationship between the source and destination ips.

Re: nested hashes and their usage e.g $foo{bar}{baz}
by crouchingpenguin (Priest) on Feb 26, 2003 at 17:13 UTC

    This situation is where I would usually use Data::Dumper to help show the entire data structure:

    #!/usr/bin/perl use strict; use warnings; use Data::Dumper; + my %srciphash = ( '10.10.10.1' => { '10.10.10.2' => 'gw', '10.10.10.3' => 'dns', '10.10.10.4' => 'www', }, '10.10.20.1' => { '10.10.20.2' => 'gw', '10.10.20.3' => 'dns', '10.10.20.4' => 'www', }, ); + foreach my $a ( keys %srciphash ){ foreach my $b ( keys %{$srciphash{$a}} ){ #print $b,"\t",$srciphash{$a}{$b},"\n"; } } + print Dumper(\%srciphash),"\n";

    outputs
    $VAR1 = { '10.10.20.1' => { '10.10.20.4' => 'www', '10.10.20.3' => 'dns', '10.10.20.2' => 'gw' }, '10.10.10.1' => { '10.10.10.2' => 'gw', '10.10.10.4' => 'www', '10.10.10.3' => 'dns' } };

    cp
    ---
    "Never be afraid to try something new. Remember, amateurs built the ark. Professionals built the Titanic."
Re: wondering what exactly these lines does
by mowgli (Friar) on Feb 26, 2003 at 17:05 UTC

    You've got a hash of hashes (sometimes called HoH) here - basically, %srciphash is a hash (an associative array) that contains references to further hashes.

    The first line you merely records whether a connection was made from a given source to a given destination ip (assuming that's what "srcip" and "dstip" stand for, which I think is pretty likely), by setting the appropriate element of the appropriate hash (itself an element in %srciphash) to 1.

    The second line later on iterates over all the source ips gathered; for that, the hash keys (the source ips) are extracted from the hash via the keys operator, and fed to a foreach loop which iterates over them one by one, assigning each of them to $srcip for the duration of the loop.

    The third line is quite similar to the second; an iteration over the keys of a hash (the destination ips for a given source ip this time) is made again, and the only real difference is that the hash these come from needs to be obtained first. In other words - since $srciphash{$srcip} (the hash value associated with the given source ip in %srciphash) is itself a reference to a hash (and thus a scalar value) instead of an immediate hash, it needs to be made into a hash again first for the keys operator to be able to work on it; that's simply done by prepending a % (and wrapping the whole thing in curly braces to avoid ambiguities). Then, a foreach loop is used again to iterate over all the destination ips for the given source ip (the keys of the hash that is the value associated with the source ip in %srciphash), so, in effect, the two nested loops just iterate over all pairs of src and destination ips that were encountered found earlier on.

    The actual value stored (the 1) is not used - it could've been any value really, too, as it just serves the purpose of making sure the given *key* actually exists in the hash.

    Hope this helps! :)

    --
    mowgli

Re: nested hashes and their usage e.g $foo{bar}{baz}
by blokhead (Monsignor) on Feb 26, 2003 at 17:00 UTC
    What you're looking at is a hash of hashes. Each source IP address is given its own hash of corresponding destinations. $srciphash{$ip} is a reference to that hash.

    I'd highly recommend looking at the Data Structures Cookbook and perlref POD pages -- these concepts are explained there better than I could hope to do.

    blokhead

Re: nested hashes and their usage e.g $foo{bar}{baz}
by pg (Canon) on Feb 26, 2003 at 17:52 UTC
    You can look at this as a two-dimension hash (in Perl world) or a two-tier tree (from a data structure view, or a real world view). This example use concepts from daily life may help you to understand it more easily:
    use strict; my %currency; $currency{northamerica}{America} = "US Dollar"; $currency{northamerica}{Canada} = "Canadian Dollar"; $currency{asia}{Japan} = "Yuen"; $currency{asia}{China} = "RMB"; foreach my $continent (keys %currency) { foreach my $country (keys %{$currency{$continent}}) { print "in $continent/$country, people use $currency{$continent +}{$country}\n"; } }
    In this case, the entities concerned (in the real world, not the computer would or Perl world) can be understand as a two-level tree, at the first level, we cut the world into continents, then at the second level, we further divide each continent into countries. In the computer world, or more precisely, in the world of Perl, we see a two-dimension hash as a good fit to represent this structure.

    This is very useful, in Perl, multi-dimensional hash is one of the useful ways to store trees.
Re: nested hashes and their usage e.g $foo{bar}{baz}
by Piercer (Beadle) on Feb 27, 2003 at 12:44 UTC
    Another way to find out what is in mystery structures is to use the debugger. run the program with -d ie perl -d foo.pl put in a breakpoint at the lines in question ie b 15 (where line 14 was your $srciphash{$srcip}{$dstip} = 1; c (continue till the breakpoint) print @srciphash - will show you that it contains a pointer not normal data. x @srciphash shows the structure and what the structure contains. Hope this helps.