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

时间:2014-01-10 18:04:47

标签: perl perl5 sigils

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

在尝试使用Perl5中的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 ) {
    print( $quota, " ", $quotas{$quota}[0], "/", $quotas{$quota}[1], " KiB\n" );
}

这段代码实际上是按照需要打印出像

这样的东西
root: user.myuser
STORAGE: 123/4567 KiB

此代码取自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], "%)");
    }
}

使用$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" );
}

然而,这段代码不起作用,我自己也没有找到任何合理的解释。我的理解是,将最后一个代码示例转移到最初发布的表单中需要将第11行中的赋值源替换为第12行中的用法,并将配额的符号从@更改为$我正在尝试获得标量结果最后。最后一个代码是在斜杠之前打印数组引用,之后没有任何内容。因此,我必须修复我的代码才能使其正常工作:

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

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

    my @sizes = $quotas{$quota};

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

    my $sizes = @quotas{$quota};

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

2 个答案:

答案 0 :(得分:5)

我相信你想要的就是你的第11行:

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

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

E.g。

use Data::Dumper;

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

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

答案 1 :(得分:5)

$quotas{$quota}访问%quotas中的单个标量元素。 @quotas{$quota}是一个哈希切片,它从哈希中选择一个元素的列表

  • $collection[...]$collection{...}:单个标量元素

    my @array = (1, 2, 3, 4);
    my $elem  = $array[1];     #=> 2
    
  • @collection[...]@collection{...}:元素列表

    my @array = (1, 2, 3, 4);
    my @slice = @array[1, 3];  #=> (2, 4)
    
  • %collection[...]%collection{...}:键值列表,除“blead”外不可用。

    my @array = (1, 2, 3, 4);
    my %kvs   = %array[1, 3];  #=> (1 => 2, 3 => 4)
    

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

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

  • 将此项指定给标量时,将在标量上下文中评估列表并提供最后一个元素:

    my $sizes = @quotas{$quota};
    my $sizes = ([...]);
    my $sizes = [...];
    [...]
    

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

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

    my @sizes = @quotas{$quota};
    my @sizes = ([...]);
    ([...])
    

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

......顺便说一下,$quotas{$quota}执行时会发生同样的情况,但原因略有不同。

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

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

所以你可以做到

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

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