Beefy Boxes and Bandwidth Generously Provided by pair Networks
Welcome to the Monastery
 
PerlMonks  

Initializing Hash Arrays in Perl

by winterwind (Initiate)
on Feb 13, 2014 at 23:38 UTC ( [id://1074915]=perlquestion: print w/replies, xml ) Need Help??

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

Hi all, I'm a PERL newbie but not a programming newb. I've been tasked with converting some PERL to PHP. I have this, what seems to be hash array being initialized on this line.

$REFERRERS{$campaign_id}{$skin_id}{$refr_url}++;

This $REFERRERS variable was not defined before this in the script. Can someone help me know whats happening here? Thank you so much.

Replies are listed 'Best First'.
Re: Initializing Hash Arrays in Perl
by davido (Cardinal) on Feb 14, 2014 at 00:01 UTC

    This is what that structure (minimally) looks like:

    %REFERRERS = ( $campaign_id => { $skin_id => { $refr_url => __some_value__ }, }, );

    ...where $campaign_id, $skin_id, and $refr_url are scalar variables that hold the names of hash keys. There really is no $REFERRERS variable (a scalar), there is a %REFERRERS, which is a hash.

    Perl has autovivification, so let's say that there doesn't yet exist a hash key named "foo". Then saying $REFERRERS{foo} = 1 causes that hash element to become available, and the value that hash element holds is 1. Let's say that your hash is entirely empty: %REFERRERS = () ...empty. By incrementing $REFERRERS{foo}{bar}{baz}++, an element inside of %REFERRERS is created, named 'foo'. Its value is set to a reference to an anonymous hash with a key named "bar", which is set to a reference to an anonymous hash with a key named "baz", and finally that element is incremented from its undefined state to "1".


    Dave

      Kenosis and Davido, you both hit the nail on the head. The purpose of it is precisely what Kenosis said as well. I understand the hash creation now but the ++ seems very odd to me. How can the $refr_url be initialized as ++? It has not been defined as anything yet. Is it something like: undef++ which evaluates to 1? I don't understand how a key's value can be incremented when it hasn't even been defined as anything yet.

        You've pretty much got it. In Perl, all scalar containers (ie, a scalar variable, an element from an array, or an element from a hash) start out as "undef", which is different from C's notion of undefined. In C, undefined means "could be anything, but never anything useful". In Perl, undefined means "undef", the value. In numeric context, undef equates to zero, meaning if you treat undef as a number it acts like zero. Increment zero, and you get 1.

        You can explicitly detect definedness with defined:

        perl -E 'say "undefined" if ! defined($var);'

        ...or...

        perl -E '$var = 1; say "defined" if defined($var);'

        But numeric operations, including Boolean comparisons in numeric context cause undef to act like a "false" numeric value, which is to say, zero.

        perl -E 'say "Hello" if undef == 0;'

        ...or...

        my $var; # Currently $var is undef print $var + 5, "\n"; # 5 print ++$var, "\n"; # 1

        That's really the less amazing thing. The more amazing thing is that you can implicitly create references to anonymous data structures. Consider this:

        my %hash; $hash{foo} = { bar => 'baz' };

        The { ... } curly braces around  bar => 'baz' act as explicit anonymous hash constructors, so the resulting structure looks like this:

        %hash = ( foo => { # { is an anon-hash constructor bar => 'baz' } );

        That's the boring explicit way. But this will create the exact same structure, without explicitly constructing the inner hashref:

        my %hash; $hash{foo}{bar} = 'baz';

        The { bar => 'baz'} hash ref was constructed implicitly.


        Dave

Re: Initializing Hash Arrays in Perl
by Kenosis (Priest) on Feb 13, 2014 at 23:52 UTC

    This is a hash of hashes of hashes (HoHoH), which could have content like:

    $VAR1 = { '543210' => { '212' => { 'http://www.webaddress.com/' => 42 } } };

    where the 543210 is a $campaign_id value, the 100 is a $skin_id value, and the http://www.webaddress.com/ is a $refr_url value.

    Please forgive the guessing here, but it looks like the data structure might be used to track the frequency of a referring URL, based upon a(n) (advertising?) campaign id and skin id.

    This structure might be queried to answer something like, "How many referrals came from http://www.webaddress.com/ during the 543210 campaign that involved skin 212?"

    Hope this helps!

Re: Initializing Hash Arrays in Perl
by sundialsvc4 (Abbot) on Feb 14, 2014 at 02:06 UTC

    What you are seeing here is a language-feature that does not, if I dimly-recall correctly, does not exist in PHP:   auto-vivification.

    (It’s pig-latin for “comes to life automagically ...”)

    Basically, it works like this:   “if Perl finds that whatever-is-required does not exist, Perl will automatically and silently create it.”   Therefore, in the start-from-nothing case, Perl will do all of the following:

    1. If $REFERRERS is not yet defined, it will automagically become a hashref, because that is what the situation requires.   (And if it is defined, but is not a hashref, “you die.”)
    2. If an entry corresponding to $campaign_id does not yet exist, it will magically appear.   And, since in the present context it needs to be “a hashref,” it will become one.
    3. ... ditto $skin_id ... (ad infinitum)
    4. ... ditto $ref_url ... however, since the operator to be applied (“++ ...”) calls for an integer, an integer with the value zero will be supplied.
    5. The integer – whether existing or new – will now be incremented.

    (Way cool, huh?!)

    All of this “convenience” is automatically done by Perl, in the name of “DWIM = Do What I Mean.™”   It may be the case that you must write PHP-specific code to do this.   (Or not ...   It’s been a few months since I’ve thought about PHP, so please don’t quote me on anything.)

      PHP does have autovivification, but only on write operations.

      <?php $arr = array(); $arr[0]['a'][1]['b'] = 2; print_r($arr); $other = array(); if ($other[0]['a'][1]['b'] == 2) { die; } else { # Note that autovivification has not happened. print_r($other); }

      Versus:

      #!/usr/bin/env perl use strict; use Data::Dumper; my @arr; $arr[0]{'a'}[1]{'b'} = 2; print Dumper \@arr; my @other; if ($other[0]{'a'}[1]{'b'} == 2) { die; } else { # Note that autovivification has happened! print Dumper \@other; }

      For the purposes of autovivification, ++ is considered a write operation.

      The Perl module autovivification allows you to have approximately the PHP behaviour:

      no autovivification;
      use Moops; class Cow :rw { has name => (default => 'Ermintrude') }; say Cow->new->name
      1. If $REFERRERS is not yet defined, it will automagically become a hashref ... [emphasis added]

      A small but, IMO, important point: In the case cited (and if strictures were not enabled; see strict), a hash named  %REFERRERS of the package global flavor would be autovivified, not a hash reference. A hash reference would be created if the  -> operator were used (see perlop).

      c:\@Work\Perl>perl -w -MData::Dump -le "$REFERRERS{foo}{bar}{baz}++; ;; dd \%REFERRERS; " { foo => { bar => { baz => 1 } } } c:\@Work\Perl>perl -w -MData::Dump -le "$REFERRERS->{foo}{bar}{baz}++; ;; dd $REFERRERS; " { foo => { bar => { baz => 1 } } }
        Wow, thanks all! It's really great here! StackOverflow can only aspire to be like this place. I understand the line fully now and see that it's all about the context in Perl.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others sharing their wisdom with the Monastery: (7)
As of 2024-04-24 09:55 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found