Beefy Boxes and Bandwidth Generously Provided by pair Networks
laziness, impatience, and hubris
 
PerlMonks  

Layman's terms how-to on HoH refs and the arrow operator

by snafu (Chaplain)
on Dec 27, 2002 at 05:34 UTC ( #222479=perlquestion: print w/replies, xml ) Need Help??

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

OK. I've been readin documentation on this and other sites and I have to admit that I am *very* frustrated with my lack of understanding to this point.

I know that there are many out there who try and read the documentation provided and find the documentation is written with so much high speak that there are those of us who don't 100% understand what they are reading. I am one of those people right now.

So, with that let me explain what I need to wrap my brain around and I sincerely hope that there is someone here who will be kind enough to explain in lamans terms the explanation. I have been working on a project that uses an HoH. Now, I know that you can reference an HoH the following way:

$hash{key}; # single dimension hash $hash{key1}{key2}; # multiple dimension hash

I understand this full-well. Ok, so what I would like to do is figure out what the heck the arrow operator does as according to the manual using the arrow operator is *not* the same as not using it (but then quickly turns around and seems to contradict itself saying that the arrow operatoris optional to use.

<quote>

Subroutine calls and lookups of individual array elements arise often +enough that it gets cumbersome to use method 2. As a form of syntactic sugar, the examples + for method 2 may be written: $arrayref->[0] = "January"; # Array element $hashref->{"KEY"} = "VALUE"; # Hash element $coderef->(1,2,3); # Subroutine call The left side of the arrow can be any expression returning a reference, including a previous dereference.
Note that $array[$x] is NOT the same thing as $array->[$x ]here:
$array[$x]->{"foo"}->[0] = "January"; This is one of the cases we mentioned earlier in which references coul +d spring into existence when in an lvalue context. Before this statement, $array[$x] +may have been undefined. If so, it's automatically defined with a hash reference so +that we can look up {"foo"}in it. Likewise $array[$x]->{"foo"}will automatically g +et defined with an array reference so that we can look up [0]in it. One more thing here.
The arrow is optional BETWEEN brackets subscripts, so you can shrink the above down to
$array[$x]{"foo"}[0] = "January"; Which, in the degenerate case of using only ordinary arrays, gives you + multidimensional arrays just like C's: $score[$x][$y][$z] += 42; Well, okay, not entirely like C's arrays, actually. C doesn't know how to grow its arrays on demand. Perl does.
</quote>

So what the freak does the arrow operator do!?!?! Because I just get confused from this documentation :). Now, Let me give you folks a little more of a view into the application that I am working on.

I have a reference to an HoH which has the structure:

$$player_dbref{player_name}{ip}{user_id};

Now I find myself in the most awkward position of wanting to add a new dimension to the hash (yes that was sarcasm). Now right now I need to actually reference the hash keys with the actual values IE $$player_dbref{happyplayer}{22.22.22.22} which would yield 265 (the player id), right? But what I decided would be easier is to actually have the hash be unique based off the first key in the hash (as usual) then to be able to reference the rest of the keys below that player key by using a topical key. Ok, so that didn't make sense so let me illustrate:

So usually I'd use $$player_dbref{happyplayer}{22.22.22.22} to get the user id and to get that I'd have to traverse the hash keys to find the name of a player and then I'd have to traverse the hash again with the player's name I just got as the key to get the IP and so on and so forth.
Is there a way to only traverse the hash keys the one time for the player name and then use the topical references such as:$$player_dbref{player_name}{ip_addr} for the player's IP and $$player_dbref{player_name}{ip_addr}{id} to get the user id where you are actually using the words 'ip_addr' and 'id' as topical references of what you want in the hash keying off the player name? Does this make sense?? :) Is this what arrow operator is for?!?! If I am right in the way I am doing it and there is no other way to do it then I will just continue doing what I am doing but if there is an easier way I'd LOVE to know what it is.

_ _ _ _ _ _ _ _ _ _
- Jim
Insert clever comment here...

update (broquaint): title change (was Lamens terms how-to on HoH refs and the arrow operator)

Replies are listed 'Best First'.
Re: Lamens terms how-to on HoH refs and the arrow operator
by chromatic (Archbishop) on Dec 27, 2002 at 06:04 UTC

    See References Quick Reference.

    The arrow operator dereferences a reference. As the documentation says, in a multi-level data structure, you can omit the arrow between certain levels.

    You do need something to disambiguate between @array and $array, where the latter contains a list reference. That's why you can use either $array->[0] or $$array[0] to access the first element. Still, see the link above.

Re: Lamens terms how-to on HoH refs and the arrow operator
by Paladin (Vicar) on Dec 27, 2002 at 06:10 UTC
    The arrow operator is optional everywhere except the first dereference.

    ie. $array[0][1] is the same as $array[0]->[1].
    In both cases, you have an array (@array) in which the first element ($array[0]) is a reference to another array.

    $array[0] is not the same as $array->[0] however.
    In the first case, you are accessing the first element of @array. In the second, you are accessing the first element of the array that is referenced by $array (note: $array is a scalar holding a reference to an array).

    As for your question about:
    $$player_dbref{player_name}{ip_addr} for the player's IP and $$player_dbref{player_name}{ip_addr}{id} to get the user id.

    You can't do that, because $$player_dbref{player_name}{ip_addr} cannot hold both the value of the players IP, and a reference to another hash.
    What you may want is:
    $$player_dbref{player_name}{ip_addr} = "1.2.3.4"; $$player_dbref{player_name}{id} = 007;
    In this case you have a reference ($player_dbref) to a hash of player names, the values of which, are another hash which holds IP, id, etc.
Re: Lamens terms how-to on HoH refs and the arrow operator
by CountZero (Bishop) on Dec 27, 2002 at 07:24 UTC

    I think you have your arrays and hashes a bit mixed up.

    So usually I'd use $$player_dbref{happyplayer}{22.22.22.22} to get the user id and to get that I'd have to traverse the hash keys to find the name of a player and then I'd have to traverse the hash again with the player's name I just got as the key to get the IP and so on and so forth.

    Actually, I don't think it works like that: happyplayer is the name of a player already: you don't have to traverse the hash to find it, you just key into it directly and its value is a reference to another hash, which is keyed by IP.
    The same goes for $$player_dbref{player_name}{ip_addr}{id}. id is already the id as key to whatever value you like to put in that slot.

    The way you have set-up this hash is such that every player can have many IP's, which each can have many ID's, which finally point to 'a value' (I can't find in your post what this value seems to be)). But you never actually store --as values that is-- names of players, their IP's or their ID's. All these data are only existing as keys into the hashes which yield you references to other hashes and finally to a value, which is not used AFAIK.

    ALso, note that every "player" has its own hash of IP-references and each IP has its own hash of ID-references. There is actually no connection between the IP's for the different players: i.e. you cannot "easily" traverse all IP-references for all players: these references are not in the same hash! The same goes for your ID-references.

    I would suggest you play around a bit with Data::Dumper to see what I mean and to see what and how things are stored in your HoH.

    CountZero

    "If you have four groups working on a compiler, you'll get a 4-pass compiler." - Conway's Law

      So usually I'd use $$player_dbref{happyplayer}{22.22.22.22} to get the user id and to get that I'd have to traverse the hash keys to find the name of a player and then I'd have to traverse the hash again with the player's name I just got as the key to get the IP and so on and so forth.
      Actually, I don't think it works like that: happyplayer is the name of a player already: you don't have to traverse the hash to find it, you just key into it directly and its value is a reference to another hash, which is keyed by IP. The same goes for $$player_dbref{player_name}{ip_addr}{id}. id is already the id as key to whatever value you like to put in that slot.

      You might have misunderstood what I was saying. My code works. In fact, I use Data::Dumper all throughout my code to watch my hash grow and shrink. What I was saying was (which works but I don't think its the best way to do it from an ease-of-code point of view) is I am using a hash reference with rvalues of the data as the keys to the hash. Thus a key to %$player_dbref would actually be referenced as $$player_dbref{$player_name}{$ip}{$id} since I don't know what the keys in the reference are yet. Therefore, I have to traverse the hash table to get the values (first the player name, then the ip, and then the id) in the HoH.

      Actually, I was able to make a change based from Paladin's comments. May I just say right now...DUH to me in reference to the idea of using $$player_dbref{$player_name}{id} and $player_dbref{$player_name}{ip} for my hash table! Why didn't I think of that?!?! =P (/me humbly kicks himself). I already sent a message to Paladin for his remarks but thank you again Paladin! So I have changed my code as thus and it works *much* better (much easier to add or subtract keys from the hash) and is way more portable. See? I was making it harder than it needed to be.

      Also, I now fully understand the arrow operator! Thanks again fellow monks!

      As a sidenote I realized something. A hash of hashes need only be as difficult as related to what needs to be unique in the table. In other words, in my code, the unique key was the player name only because that will always be unique in the game (the IP won't always be because of multiple clients running on the client machine and the id would be but I haven't made a clear decision to key off that value yet). Thus, for tracking a player if I don't know what a player's name is I need only traverse the hash for a particular player's name and from there I now have everything I need for that player; the id, the ip, eventually the map, and anything else I need to know about that player.

      _ _ _ _ _ _ _ _ _ _
      - Jim
      Insert clever comment here...

Re: Lamens terms how-to on HoH refs and the arrow operator
by pg (Canon) on Dec 27, 2002 at 06:18 UTC
    If you feel really confused, just do this:

    use -> after ref, and don't use it otherwise

    use Data::Dumper; use strict; my %a = (1,2,3,4); my $a_ref = \%a; print "not ref, without ->\n"; print $a{1}, "\n";#good print $a{3}, "\n";#good print "not ref, with ->\n"; print $a->{1}, "\n";#uninitialized print $a->{3}, "\n";#uninitialized print "ref, without ->\n"; #print $a_ref{1}, "\n"; #cannot uncomment #print $a_ref{3}, "\n"; #cannot uncomment print "ref, with ->\n"; print $a_ref->{1}, "\n";#good print $a_ref->{3}, "\n";#good
Re: Layman's terms how-to on HoH refs and the arrow operator
by roundboy (Sexton) on Dec 27, 2002 at 19:00 UTC

    One thing I find helpful when dealing with complex data structures is to break it down one layer at a time, and get a clear understanding of what each element of each structure actually looks like. You can pretend, for the purposes of this exercise, to be doing OO-Perl, and give a name to the "class" of each element, which can help you draw pictures or write explanations.

    So, for your code (assuming I'm understanding it properly):

    1. %$player_dbref is a map from player names to let's call them Player objects. So
      $player = $player_dbref->{$a_name};

      To answer your original question, this is the only place where the arrow operator is necessary. But because this is Perl, and TMTOWTDI, you can (and have been) replacing $hr->{k} with $$hr{k}, which is the same thing.

    2. Now from your example, it looks like a Player (a person with a particular name) can log in from multiple places, each resulting in in what I'll call a PlayerInstance. Thus:
      $player_inst = $player->{$an_ip};

      Combining this with the previous item yields
      $player_inst = $player_dbref->{$a_name}->{$an_ip}
      or
      $player_inst = $player_dbref->{$a_name}{$an_ip}
      since you can eliminate the arrow after the first dereference.

    3. Finally, a PlayerInstance is what? Well, in your example you don't show anything other than an id, so if you really don't have any other data to store, you don't even need another bunch of references, but have $player_inst above be the (numeric) userid value.

      However, I'm going to assume you have other information to store, and then there are two possibilities:

      1. There is only one possible id for each name/ip combination, in which case a player instance is just a (reference to a ) hash from data keys to values, for example:

        $id = $player_inst->{id}; $hitpoints = $player_inst->{hp}; $weapon = $player_inst->{weapon};

        Let's call one of these things a PlayerData

      2. The other possibility is that you have multiple possible IDs per name/ip combination. This seems likely, given your example. In that case, a player instance is a map from IDs to PlayerData things. Then:

        $playerdata = $player_inst->{$an_id}; $hitpoints = $playerdata->{hp};
        <it>etc</it>.

      Now, you can combine this with the previous items, and get:
      $hitpoints =  $player_dbref->{$a_name}{$an_ip}{$an_id}{hp}

    But the nice thing is that all those intermediate steps are legal, and there's no reason not to do them if you have a need to. So:

    To find all the IPs for a particular player
    keys %$player, or keys %{$player_dbref->{$a_name}}
    To find all the IDs for a player at a particular IP (assuming there's more than one)
    keys %$player_inst, or
    keys %{ $player->{$an_ip} }, or
    keys %{ $player_dbref->{$a_name}{$an_ip} }

    HTH!

    --roundboy

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://222479]
Approved by djantzen
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (4)
As of 2019-07-17 17:01 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?