【问题标题】:Perl: not an array reference while calculating array sizePerl:计算数组大小时不是数组引用
【发布时间】:2019-04-19 15:06:42
【问题描述】:

我正在尝试解开一些遗留代码,如果它的大小大于 x(其中 x 是硬编码的 int),则对 $value 进行操作。这是它目前的样子:

if (scalar(@{$value}) > x) {
    ...
}

与所有遗留代码一样,这个 $value 几乎可以是任何东西(散列、标量、数组),尽管它应该是不同对象的数组。这段代码目前在大约 5% 的时间出现“不是数组引用”而失败,我试图找出可能破坏它的 $value 是什么。

我认为如果 $value 未定义它可能会失败,所以我什至给它一个 || [] 但无济于事(同样的错误):

if (scalar(@{$value || []}) > x) {
    ...
}

我也想弄清楚为什么我需要@{}?我的理解是,它在列表上下文中评估 $value ,以便标量以后可以确保我得到大小。这会使代码更健壮还是我可以直接使用标量 $value? @{} 甚至会做我认为的事情吗?

我正在使用 perl 5.8。

【问题讨论】:

  • $value 应该是对数组的引用。 @$value 是 $value 引用的数组。 scalar(@$value) 是该数组中包含的元素数。 scalar($value) 是一个类似 "ARRAY(0xce1460)" 的字符串。
  • “我使用的是 perl 5.8。” 为什么,哦,为什么? :-(
  • @DaveMitchell,先生,您刚刚重新连接了我的大脑。谢谢你。我不知道这会是一个参考。我只是假设 perl 做了一些疯狂的连接来把它变成一个标量上下文。太棒了!
  • @DaveCross 您不选择的两件事。您的父母以及当前运行您的旧系统的 perl 版本 :) 我感谢您的祈祷,哈哈

标签: perl perl5.8


【解决方案1】:

你有两个问题。我将分别回答:

问题:@{} 在做什么?

当您写入@{$thing} 时,您将取消引用 $thing 到一个数组中。正如您所注意到的,这仅在 $thing 是数组引用时才有效。 (其他解引用运算符是 %{$thing} 用于散列引用,${$thing} 用于标量引用。)

确实需要一个解引用操作符。考虑一下:

my $arrayref = [ 'Alice', 'Bob', 'Charlie' ];
my $hashref  = { x => 4, y => 5 };
my $string   = "Hello, world";
for my $thing ($arrayref, $hashref, $string) {
    print "thing         --> ", $thing, "\n";
    print "scalar(thing) --> ", scalar($thing), "\n";
}

输出:

thing         --> ARRAY(0x7f3b8054e468)
scalar(thing) --> ARRAY(0x7f3b8054e468)
thing         --> HASH(0x7f3b80560678)
scalar(thing) --> HASH(0x7f3b80560678)
thing         --> Hello, world
scalar(thing) --> Hello, world

$thing 强制为标量上下文是没有意义的。 它已经是一个标量了!

问题:如何安全地取消对标量的引用?

如果您不知道$thing 中包含什么样的引用,您可以使用Ref::Util 进行检查:

use Ref::Util qw( is_arrayref is_hashref );

for my $thing ($arrayref, $hashref, $string) {
    if (is_arrayref($thing)) {
        print "array: thing         --> ", @{$thing}, "\n";
        print "array: scalar(thing) --> ", scalar(@{$thing}), "\n";
    }
    elsif (is_hashref($thing)) {
        print "hash:  thing         --> ", %{$thing}, "\n";
        print "hash:  scalar(thing) --> ", scalar(%{$thing}), "\n";
    }
    else
    {
        print "else:  thing         --> ", $thing, "\n";
    }
}

输出:

array: thing         --> AliceBobCharlie
array: scalar(thing) --> 3
hash:  thing         --> y5x4
hash:  scalar(thing) --> 2/8
else:  thing         --> Hello, world

观察:

  • 当解除引用时,arrayref 成为其元素的列表。 print 输出没有分隔符的每个元素:AliceBobCharlie
  • 当解除引用并强制转换为标量时,arrayref 变为元素的数量:3
  • hashref 在取消引用时会变成键和值的列表。 print 输出每对没有分隔符:y5x4
  • hashref 在取消引用并强制转换为标量时,会变为 字符串,其中第一个数字是键数,第二个数字是哈希表中的桶数:2/8

【讨论】:

  • 解释得很漂亮。谢谢!阅读上面 Dave Mitchell 的评论,您似乎也可以在没有 {} 的情况下执行 scalar(@$value)。这是否意味着 {} 就像一个匿名子例程?还是提供其他功能?
  • @AbdoSalem @$value@{$value} 完全相同...只是语法糖。 :) 当您必须消除对嵌套在其他对象内的 arrayref 的访问歧义时,大括号很有用(有时需要):@{ $thing->{foo}->{bar} }
  • 太棒了!非常感谢您的详细回答! :)
  • @ysth 那是一个迷你宝藏。谢谢!
【解决方案2】:

以下代码将解决:

if ($value and ref $value eq 'ARRAY' and @$value > x) { ... }

基本上,只有当它是一个 ARRAY 引用时,您才能取消对 ARRAY 的引用。所以确保它是一个 ARRAY ref 是必须的,这样它就不会因为 HASH 等而失败

【讨论】:

    猜你喜欢
    • 2010-10-17
    • 1970-01-01
    • 2017-08-11
    • 2019-08-28
    • 2013-11-27
    • 2018-08-26
    • 1970-01-01
    • 2016-06-16
    相关资源
    最近更新 更多