关于如何在 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"(未测试)。
您应该注意,您使用了错误的格式来分配哈希值:
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
具有三个元素,键为 name
、people
和 products
。他们的价值是
$data{name}
,这是一个标量字符串。它不是参考,因此无法进一步访问。
$data{people}
,这是一个散列引用,因此结构还有另一层。内部散列只有一个键 lead
,它的值通过向散列引用添加另一个 {...}
索引来访问:
$data{people}{lead}
这导致标量字符串 Tom
,因此无法进一步访问。
$data{products}
,这是一个数组引用,因此结构还有另一层。内部数组有两个索引,0 和 1,它们的值通过向数组引用
添加另一个 [...]
索引来访问
$data{products}[0]
和
$data{products}[1]
这些导致标量字符串 p1
和 p2
,因此无法进一步访问。
我希望从这里可以清楚地了解如何访问任意嵌套数据结构的值。
同样值得解释的是,提取对临时标量变量的中间引用通常是有利的,尤其是在您使用嵌套循环时。例如,您可以写
my $products = $data{products};
之后,您可以只说 $products->[0]
和 $products->[1]
,而不是像上面那样每次都从数据结构的顶部列出所有键。请注意,通过引用访问数组和散列的语法使用箭头 间接运算符 。你可以类似地写
my $people = $data{people};
然后
print $people->{lead}, "\n";
希望对您有所帮助。
在 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"(未测试)。
您应该注意,您使用了错误的格式来分配哈希值:
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
具有三个元素,键为 name
、people
和 products
。他们的价值是
$data{name}
,这是一个标量字符串。它不是参考,因此无法进一步访问。
$data{people}
,这是一个散列引用,因此结构还有另一层。内部散列只有一个键 lead
,它的值通过向散列引用添加另一个 {...}
索引来访问:
$data{people}{lead}
这导致标量字符串 Tom
,因此无法进一步访问。
$data{products}
,这是一个数组引用,因此结构还有另一层。内部数组有两个索引,0 和 1,它们的值通过向数组引用
[...]
索引来访问
$data{products}[0]
和
$data{products}[1]
这些导致标量字符串 p1
和 p2
,因此无法进一步访问。
我希望从这里可以清楚地了解如何访问任意嵌套数据结构的值。
同样值得解释的是,提取对临时标量变量的中间引用通常是有利的,尤其是在您使用嵌套循环时。例如,您可以写
my $products = $data{products};
之后,您可以只说 $products->[0]
和 $products->[1]
,而不是像上面那样每次都从数据结构的顶部列出所有键。请注意,通过引用访问数组和散列的语法使用箭头 间接运算符 。你可以类似地写
my $people = $data{people};
然后
print $people->{lead}, "\n";
希望对您有所帮助。