Perl 5中的数据访问不一致(愚蠢?)(也使我对使用sigils感到困惑)

Tho*_*ban 1 perl sigils perl5

这个问题是关于要求对Perl系统中发生的事情进行一些解释,因为我现在编码的时间超过25年并没有隐含地看到这一点.故事来了......

在尝试使用Cyrus::IMAP::AdminPerl5中的实例时,我试图获取并打印一个配额列表,导致返回一些奇怪的结构化数据.

my %quotas = $client->listquota(@list[0]);

if ( $client->error ) {
    printf STDERR "Error: " . $client->error . "\n";
    exit 1;
}

print "root: " . $list[0] . "\n";

foreach my $quota ( keys %quotas ) {
    print( $quota, " ", $quotas{$quota}[0], "/", $quotas{$quota}[1], " KiB\n" );
}
Run Code Online (Sandbox Code Playgroud)

这段代码实际上是通过打印出类似的东西来实现的

root: user.myuser
STORAGE: 123/4567 KiB
Run Code Online (Sandbox Code Playgroud)

此代码取自以下Cyrus::IMAP::Shell类似的读取:

my %quota = $$cyrref->listquota(@nargv);
foreach my $quota (keys %quota) {
    $lfh->[1]->print(" ", $quota, " ", $quota{$quota}[0], "/", $quota{$quota}[1]);
    if ($quota{$quota}[1]) {
        $lfh->[1]->print(" (", $quota{$quota}[0] * 100 / $quota{$quota}[1], "%)");
    }
}
Run Code Online (Sandbox Code Playgroud)

这段代码对我来说看起来有些愚蠢$quota{$quota}[0].在我的例子中,我重命名了一些变量来拒绝混合使用不同类型但等价命名的变量.

在获取代码之前,Cyrus::IMAP::Admin我试图理解其规范并通过自己编写的代码处理结果.它看起来像这样:

my %quotas = $client->listquota(@list[0]);

if ( $client->error ) {
    printf STDERR "Error: " . $client->error . "\n";
    exit 1;
}

print "root: " . $list[0] . "\n";

foreach my $quota ( keys %quotas ) {
    my @sizes = @quotas{$quota};
    print( $quota, " ", $sizes[0], "/", $sizes[1], "\n" );
}
Run Code Online (Sandbox Code Playgroud)

但是,这段代码不起作用,我自己也没有找到任何合理的解释.我的理解是,将最后一个代码示例转换为最初发布的表单需要将第11行中的赋值源替换为第12行中的使用,并将配额的sigil更改@$for,因为我试图最终获得标量结果.最后一个代码是在斜杠之前打印数组引用,之后没有任何内容.所以我必须像这样修复我的代码才能让它工作:

my %quotas = $client->listquota(@list[0]);

if ( $client->error ) {
    printf STDERR "Error: " . $client->error . "\n";
    exit 1;
}

print "root: " . $list[0] . "\n";

foreach my $quota ( keys %quotas ) {
    my @sizes = @quotas{$quota};
    print( $quota, " ", $sizes[0][0], "/", $sizes[0][1], "\n" );
}
Run Code Online (Sandbox Code Playgroud)

第12行中的这个额外解除引用是我现在感到困惑的.为什么@sizes包含一个存储另一个数组的数组在其唯一的第一个元素中 由于感到困惑,我已经在第11行尝试了替代代码,但无济于事.这些测试包括

    my @sizes = $quotas{$quota};
Run Code Online (Sandbox Code Playgroud)

(因为它与上面公布的原始代码等效)和

    my $sizes = @quotas{$quota};
Run Code Online (Sandbox Code Playgroud)

(因为我不知道为什么).切换符号似乎根本不会改变赋值的语义.但是使用这个赋值似乎打开了%quotas最初包含的数据结构的不同视图.需要哪些符号才能具有最顶层代码片段中使用的@sizes匹配内容和结构$quotas{$quota}

use*_*018 5

我相信你想在你的第11行:

my @sizes = @{ $quotas{$quota} };

此外,建议您开始在任何地方使用Data :: Dumper.

例如

use Data::Dumper;

print 'Data structure of \%quotas: ' . Dumper(\%quotas) . qq(\n);

这样你就可以确定你正在处理什么结构.


amo*_*mon 5

$quotas{$quota}访问单个标量元素%quotas.@quotas{$quota}是一个散列切片,其选择一个列表从散列一个要素.

在引用集合中的元素时使用另一个sigil不会取消引用该元素!

现在或单元素列表@quotas{$quota}包含什么?它是一个数组引用 [...].

  • 将此值分配给标量时,将在标量上下文中评估列表并给出最后一个元素:

    my $sizes = @quotas{$quota};
    my $sizes = ([...]);
    my $sizes = [...];
    [...]
    
    Run Code Online (Sandbox Code Playgroud)

    访问该数组引用中的元素然后看起来像$sizes->[0].

  • 将此值分配给数组时,您将创建一个数组,该数组将此数组引用保存为单个元素:

    my @sizes = @quotas{$quota};
    my @sizes = ([...]);
    ([...])
    
    Run Code Online (Sandbox Code Playgroud)

    访问该数组引用中的元素然后看起来像$sizes[0][0],因为您首先必须获取该数组内的该数组引用@sizes.

......顺便说一下,当你这样做时$quotas{$quota},会发生同样的事情,但原因略有不同.

如果要取消引用数组的数组引用,请使用花括号:

  • my @foo = @{$array_refernce} 复制内容
  • my $elem = ${$array_reference}[0]访问一个元素,相同$array_reference->[0].

所以你可以做到

my @sizes = @{ $quotas{$quota} };
Run Code Online (Sandbox Code Playgroud)

取消引用数组并将其复制到数组变量.然后,您可以访问类似的元素$sizes[0].