1

这个问题是关于要求对 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" );
}

通过打印出类似的内容,此代码实际上可以按需要工作

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}最顶层代码片段中使用的内容和结构?

4

2 回答 2

5

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

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

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

例如

use Data::Dumper;

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

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

于 2014-01-10T18:28:56.730 回答
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[...]or %collection{...}: key-value 列表,除了“blead”之外尚不可用。

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

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

现在 or 单元素列表@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].

于 2014-01-10T18:29:21.687 回答