简体   繁体   中英

easy rule about how to access the nested structures in perl

While using the nested structures in perl, I found it a little difficult for me to write right code to access an element in the nested structures, especially which symbol I should use, for example:

my %data = {
    name => 'G1',
    people => {
        lead => 'Tom',
    },
    products => ['p1', 'p2'],
};

print "Want Tom: ${$data{'people'}}{'lead'}\n";

perl test.pl

Want Tom: 

How can I get 'Tom' in this nested structures? or how can I understand that better to know when use what symbols to do that?

You should note that you are using the wrong format to assign to a hash:

my %data = { 
#          ^--- WRONG!

When assigning to a hash, use parentheses:

my %data = (

Using { ... } will return a reference to a hash, which is a scalar value. It will give you the warning Reference found where even-sized list expected if you have use warnings enabled (which you always should). The resulting structure will look like this:

my %data = ( "HASH(0x50daf8)" => undef );

Meaning the reference was stringified, and since the assignment only had one value, the value of this key is undefined.

You can use this if your intent is to use a hash reference:

my $href = { ....

Explanation

This statement:

${$test{'people'}}{'lead'}

Means this:

${ ... } # this is a dereference of a reference

Which means that:

$test{'people'}

Must return a reference to a hash. Meaning it is at least two-dimensional.

You then take one of the keys from the dereferenced hash:

${ .... }{'lead'}

This should work, although it is a circumspect way of writing. Note that your code has two problems mentioned above, which is why it does not work.

Solution

Besides assigning to your hash the correct way, using parentheses, and using the correct name of your variable, this is how you take a value from a hash, at its simplest:

$data{people}{lead}

The -> syntactic sugar makes it easy to understand your structure:

use strict;
use warnings;
use feature qw(say);

use Data::Dumper;

my %data = (                       # Hashes use (...) and not {...}
    name => 'G1',
    people => {
        lead => 'Tom',
    },
    products => ['p1', 'p2'],
);

say "Tom is " . $data{people}->{lead};

First, when you define a hash, you use (...) . The {...} defines a hash reference . What you had wasn't strictly legal. You could have done this:

my $data = {                       # This is now a hash reference!
    name => 'G1',
    people => {
        lead => 'Tom',
    },
    products => ['p1', 'p2'],
};
say "Tom is " . $data->{people}->{lead};

Instead of using %data = (...) , this uses $data = {...} . Note that $data is a scalar that contains a hash reference! Note how this uses $data->{people}... instead of $data{people}.

Think of -> as meaning points to . $data points to a hash that has the key {people} . This points to another hash that has the key lead .

Since you know references, you should take the next step and learn Object Oriented Perl . You can think of Object Oriented Perl as mere syntactic sugar over references. You basically use subroutines (called methods in Object Oriented Programming) to help set and get the data in your object

Here's your program again rewritten in OO Perl. Note how your complex data structure is a lot easier to manipulate because we let the subroutines ... I mean the methods make manipulating the structure much easier to track.

use strict;
use warnings;
use autodie;
use feature qw(say);

my $g1 = Local::Project->new( 'G1' );
$g1->person( 'lead', 'Tom' );
$g1->product( 'p1' );
$g1->product( 'p2' );

say "The name of the project is " . $g1->name;
say "The lead of the project is " . $g1->person('lead');
say "The products on the project are " . $g1->product;


package Local::Project;

sub new {
    my $class   = shift;
    my $name    = shift;

    my $self = {};
    bless $self, $class;
    $self->name($name);

    return $self;
}

sub name {
    my $self        = shift;
    my $name        = shift;

    if ( defined $name ) {
        $self->{NAME} = $name;
    }
    return $self->{NAME};
}

sub person {
    my $self    = shift;
    my $title   = shift;
    my $name    = shift;

    if ( defined $name ) {
        $self->{PEOPLE}->{$title} = $name;
    }
    return $self->{PEOPLE}->{$title};
}

sub product {
    my $self    = shift;
    my $product = shift;

    $self->{PRODUCT} = [] if not defined $self->{PRODUCT};
    if ( defined $product ) {
        push @{ $self->{PRODUCT} }, $product;
    }
    # return @{ $self->{PRODUCT} } -- Let's give the user a choice!
    my @products = @{ $self->{PRODUCT} };
    return wantarray ? @products : join ", ", @products;
}

UPDATE : I thought it would be nice to return a project name. Plus, I added the ability to return the products in a list as a comma separated string. This makes my main program a bit easier to follow. I don't have to join the returned array. I can simply ask my method to do it for me.

You'll probably want methods to track whether a product is in the project, be able to remove people and projects from the structure etc. However, this points out another great thing about OO Perl. You can change how the class itself works, but the program itself doesn't have to be modified.

For example, it would be nice to know if a product is in a project. It would be nice then to use a hash to store my products instead of an array. I could modify my product method in my class to use a hash, and I don't have to modify my program itself. If I used raw references, I'd be going though my entire program looking for where I use an array for my products and changing it to a hash.

{} returns a hash reference. You shouldn't assign a hash reference to a hash ( %data ), you should use a normal parentheses () .

$data{people}{lead} should return "Tom" (untested).

As @TLP mentioned, use strict and warnings to avoid such problems.

Perhaps this will help. Looking at your data

my %data = (
    name     => 'G1',
    people   => {
        lead     => 'Tom',
    },
    products => ['p1', 'p2'],
);

The hash %data has three elements, with keys name , people , and products . Their values are

$data{name} , which is a scalar string. It isn't a reference, and so cannot be accessed further.

$data{people} , which is a hash reference, and so there is another level to the structure. The inner hash has just one key, lead , and its value is accessed by adding another {...} index to the hash reference:

$data{people}{lead}

This leads to the scalar string Tom , and so cannot be accessed further.

$data{products} , which is an array reference, and so there is another level to the structure. The inner array has two indexes, 0 and 1, and their values are accessed by adding another [...] index to the array reference

$data{products}[0]

and

$data{products}[1]

These lead to the scalar strings p1 and p2 , and so cannot be accessed further.

I hope it is clear from this how to access the values of any arbitrarily nested data structure.

It is also worth explaining that it can often be advantageous to extract intermediate references to temporary scalar variables, especially if you are using nested loops. For instance, you could write

my $products = $data{products};

after which, instead of listing all the keys from the top of the data structure each time, as above, you can say just $products->[0] and $products->[1] . Note the syntax for accessing arrays and hashes through their references uses the arrow indirection operator . You could similarly write

my $people = $data{people};

and then

print $people->{lead}, "\n";

I hope this helps.

[The question has already been answered. This is just some additional information for the OP]

${ EXPR }{lead}

is a hash element dereference. It can also be written as

EXPR->{lead}

so

${$test{people}}{lead}

can also be written as

$test{people}->{lead}

-> can be omitted between two indexes, so the above could also be written as

$test{people}{lead}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM