如何在哈希中的数组中引用Perl哈希?

3
这是我正在处理的代码片段:

这是我正在处理的代码片段:

my %photo_details = (
 'black_cat' => (
  ('size' => '1600x1200', 'position' => -25),
  ('size' => '1280x1024', 'position' =>  25),
  ('size' =>   '800x600', 'position' =>   0),
 ),
 'race_car' => (
  ('size' => '1600x1200', 'position' =>  10),
  ('size' =>   '800x600', 'position' =>   5),
 ),
);

my $photo = 'black_cat';

foreach my $photo_detail ($photo_details{$photo})
{
 my $size     = $photo_detail{'size'};
 my $position = $photo_detail{'position'};

 print ("size = $size, position = $position\n");
}

我期望得到的是:

大小 = 1600x1200,位置 = -25

大小 = 1280x1024,位置 = 25

大小 = 800x600,位置 = 0

我实际得到的是:

在 C:\Test.pl 的第23行使用未初始化的值 $size 进行字符串连接 (.) 或字符串 at。

在 C:\Test.pl 的第23行使用未初始化的值 $position 进行字符串连接 (.) 或字符串 at。

大小 = ,位置 =

循环语句明显有误,因为$size和$position都没有值,并且只循环了一次而不是三次。我尝试了各种变量前缀,但都没有起作用。
我做错了什么?
3个回答

15

首先,始终使用以下内容启动每个脚本或模块:

use strict;
use warnings;
你将会获得更多的警告信息且更早,这将极大地有助于调试。
我无法复制你的错误:当我将代码放入文件中并不添加任何标志运行时,我得到的是:size =,position =。错误消息与你打印的代码中没有$size变量,因此不能匹配。
尽管如此,你声明数据结构的方式是错误的。哈希和数组只能包含标量值,而不能包含列表:因此,如果想嵌套一个数组或哈希,则需要使其成为引用。关于数据结构和引用,请参见perldoc perldataperldoc perldscperldoc perlreftut
my %photo_details = (
    black_cat => [
        { size => '1600x1200', position => -25 },
        { size => '1280x1024', position =>  25 },
        { size => '800x600', position => 0 },
    ],
    race_car => [
        { size => '1600x1200', position =>  10 },
        { size => '800x600', position =>   5 },
    ],  
);

foreach my $photo_detail (@{$photo_details{black_cat}})
{
    my $size     = $photo_detail->{size};
    my $position = $photo_detail->{position};

    print ("size = $size, position = $position\n");
}

我有点困惑,$size在他发布的代码片段中肯定是存在的,你甚至将其带入了你的代码。你的意思是什么? - lexu
@lexu:是的,你说得对;我认为错误可能来自于使用$photo_detail->{$size}而不是$photo_detail->{size}。我完全忽略了my $size = ...声明。尽管如此,我无法复制OP的错误。启用严格模式后,代码会在更早的时候出错,因为%photo_detail没有被声明(由于for循环没有对数组进行解引用)。 - Ether
谢谢您的解释。数据结构现在看起来肯定是正确的。但是,如果我尝试上面的代码,第20行和21行会出现“全局符号“%photo_detail”需要显式包名称”的错误。我阅读了文档(感谢您),并将foreach语句更改为[foreach my $photo_detail (@{%photo_details->{black_cat}})],将[$photo_detail{size}]更改为[$photo_detail->{size}],将[$photo_detail{position}]更改为`[$photo_detail->{position}],它可以工作,但是在foreach行上我得到了“使用哈希作为引用已被弃用”的警告。有什么想法吗? - TallGuy
@Ether 我并不是在批评你的逻辑,只是关于 $size 这个表达让我有些困惑!原帖的作者似乎正在学习符号和作用域的使用……这两者一开始确实很令人困惑。对你的好建议给个赞(点赞)! - lexu
@TallGuy:在“perldoc perlwarn”中查找该警告消息是很有信息量的:您在foreach行上有一个标点符号错误。 - Ether

15

这里是一些更新的代码,下方有解释:

#!/usr/bin/perl

use strict;
use warnings;

use Data::Dumper;

my %photo_details = (
    'black_cat' => [
        {'size' => '1600x1200', 'position' => -25},
        {'size' => '1280x1024', 'position' =>  25},
        {'size' =>   '800x600', 'position' =>   0},
    ],
    'race_car' => [
        {'size' => '1600x1200', 'position' =>  10},
        {'size' =>   '800x600', 'position' =>   5},
    ],
);


print Dumper( %photo_details );
foreach my $name ( keys %photo_details ) {
    foreach my $photo_detail ( @{ $photo_details{$name} } ) {
        my $size     = $photo_detail->{'size'};
        my $position = $photo_detail->{'position'};

        print Dumper( $photo_details{$photo} );

        print ("size = $size, position = $position\n");
    }
}

我已经用方括号和花括号替换了一些圆括号。在Perl中,方括号给你一个匿名数组的引用,而花括号表示对匿名哈希的引用。它们被称为匿名,因为没有显式的变量名来命名这个匿名数组或哈希。

由于Perl数据结构要求你存储对哈希的引用而不是实际的哈希,所以你需要使用这些来构造引用。你可以像这样分两步进行:

my @array = ( 1, 2, 3 );
my $array_ref = \@array;
my %hash = ( 'one' => 1, 'two' => 2, 'three' => 3 );
my $hash_ref = \%hash_ref;

要从$array_ref和$hash_ref中获取数据,您需要使用->运算符:

print $array_ref->[0], "\n";
print $hash_ref->{one}, "\n";

当引用哈希键时,你不需要在{}中加引号,尽管有些人认为在哈希键上使用引号是一种良好的习惯。

我添加了一个遍历整个数据结构而不仅是查看一个引用的示例。以下是第一行:

foreach my $name ( keys %photo_details ) {

keys方法返回哈希表中的所有键,以便您可以按顺序获取它们。下一行迭代%photo_details中所有photo_detail哈希引用:

    foreach my $photo_detail ( @{ $photo_details{$photo} } ) {

@{ $photo_details{$photo} } 将引用 $photo_details{$photo} 解引用为一个数组,您可以使用 foreach 对其进行迭代。

我添加的最后一件事是调用Data::Dumper,它是与Perl一起分发的非常有用的模块,可为您打印数据结构。在构建类似这样的数据结构时,这非常方便,它的紧密相关的表兄弟Data::Dumper::Simple也很有用。不幸的是,这个模块没有与Perl一起分发,但我喜欢它的输出,因为它包括变量名。

如果想进一步了解如何使用引用构建复杂的数据结构,请查阅perlreftut


你的意思是 $photo_details{$name} 吗? - nodmonkey
my $hash_ref = \%hash_ref; - that should be \%hash - Richlv
我这里只有一个问题:我们可以为了描述目的将这个数据结构称为“哈希数组哈希”吗? - user3138373

2

你需要关注的只有数据结构的顶层。之后,你只需要为每个级别使用正确的索引语法:

如果你有一个常规哈希表,你可以访问你想要的键,然后在其后面对齐每个级别的额外索引:

 %regular_hash = ...;
 $regular_hash{$key}[$index]{$key2};

如果您有一个引用,那么您几乎可以做同样的事情,但是您必须从顶级引用开始使用箭头->进行初始取消引用。之后是相同的索引序列:

 $hash_ref = ...;
 $hash_ref->{$key}[$index]{$key2};

关于所有细节,请参见《Intermediate Perl》,我们在其中解释了引用语法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接