关于如何在 perl 中访问嵌套结构的简单规则

easy rule about how to access the nested structures in perl

在 perl 中使用嵌套结构时,我发现编写正确的代码来访问嵌套结构中的元素有点困难,尤其是我应该使用哪个符号,例如:

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

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

perl test.pl

Want Tom: 

如何在这个嵌套结构中得到 'Tom'?或者我怎样才能更好地理解什么时候使用什么符号来做到这一点?

{} return 是哈希引用。您不应该将哈希引用分配给哈希 (%data),您应该使用普通括号 ().

$data{people}{lead} 应该 return "Tom"(未测试)。

如@TLP所述,使用strict and warnings可以避免此类问题。

您应该注意,您使用了错误的格式来分配哈希值:

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

分配给散列时,使用括号:

my %data = (

使用 { ... } 将 return 对散列的引用,这是一个标量值。如果您启用了 use warnings(您始终应该启用),它会给您警告 Reference found where even-sized list expected。生成的结构将如下所示:

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

意味着引用被字符串化了,因为赋值只有一个值,所以这个键的值是未定义的。

如果您打算使用哈希引用,则可以使用它:

my $href = { ....

说明

这条语句:

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

意思是:

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

这意味着:

$test{'people'}

必须 return 对哈希的引用。这意味着它至少是二维的。

然后您从取消引用的散列中获取其中一个键:

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

这应该可行,尽管它是一种谨慎的写作方式。请注意,您的代码存在上述两个问题,这就是它不起作用的原因。

解决方案

除了以正确的方式为您的散列赋值、使用括号和使用正确的变量名称之外,这就是您从散列中获取值的最简单方式:

$data{people}{lead}

[问题已经回答。这只是 OP 的一些附加信息]

${ EXPR }{lead}

是散列元素取消引用。也可以写成

EXPR->{lead}

所以

${$test{people}}{lead}

也可以写成

$test{people}->{lead}
两个索引之间可以省略

->,所以上面也可以写成

$test{people}{lead}

->语法糖让你的结构更容易理解:

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};

首先,当你定义一个散列时,你使用(...){...} 定义了一个 散列引用 。你所拥有的并不完全合法。你可以这样做:

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

不使用 %data = (...),而是使用 $data = {...}。请注意,$data 是一个包含哈希引用的 标量 !请注意这是如何使用 $data->{people}... 而不是 $data{people}.

认为 -> 的意思是 指向 $data 指向 具有键 {people} 的散列。此 指向 另一个具有密钥 lead.

的散列

既然知道参考文献,就应该下一步学习Object Oriented Perl。您可以将 Object Oriented Perl 视为引用的语法糖。您基本上使用子例程(在面向对象编程中称为 方法 )来帮助设置和获取对象中的数据

这是用 OO Perl 再次重写的程序。请注意你的复杂数据结构是如何更容易操作的,因为我们让 子例程 ... 我的意思是 方法 使操作结构更容易跟踪。

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.

您可能需要一些方法来跟踪产品是否在项目中,能够从结构中删除人员和项目等。但是,这指出了 OO Perl 的另一个优点。您可以更改 class 本身的工作方式,但不必修改程序本身。

例如,很高兴知道某个产品是否在项目中。那么使用散列而不是数组来存储我的产品会很好。我可以在我的 class 中修改我的 product 方法以使用散列,而且我不必修改我的程序本身。如果我使用原始引用,我将遍历我的整个程序,寻找我在何处为我的产品使用数组并将其更改为散列。

也许这会有所帮助。查看您的数据

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

散列 %data 具有三个元素,键为 namepeopleproducts。他们的价值是

$data{name},这是一个标量字符串。它不是参考,因此无法进一步访问。

$data{people},这是一个散列引用,因此结构还有另一层。内部散列只有一个键 lead,它的值通过向散列引用添加另一个 {...} 索引来访问:

$data{people}{lead}

这导致标量字符串 Tom,因此无法进一步访问。

$data{products},这是一个数组引用,因此结构还有另一层。内部数组有两个索引,0 和 1,它们的值通过向数组引用

添加另一个 [...] 索引来访问
$data{products}[0]

$data{products}[1]

这些导致标量字符串 p1p2,因此无法进一步访问。

我希望从这里可以清楚地了解如何访问任意嵌套数据结构的值。

同样值得解释的是,提取对临时标量变量的中间引用通常是有利的,尤其是在您使用嵌套循环时。例如,您可以写

my $products = $data{products};

之后,您可以只说 $products->[0]$products->[1],而不是像上面那样每次都从数据结构的顶部列出所有键。请注意,通过引用访问数组和散列的语法使用箭头 间接运算符 。你可以类似地写

my $people = $data{people};

然后

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

希望对您有所帮助。