为什么 "Type of argument to each on reference must be unblessed hashref or arrayref at test.pl line 17."?

Why "Type of argument to each on reference must be unblessed hashref or arrayref at test.pl line 17."?

我不明白错误信息

Type of argument to each on reference must be unblessed hashref or arrayref at test.pl line 17.

对于这个测试用例(从一个更大的模块中提取):

BEGIN { our $RE_timestamp = qr/whatever/; }

# check Regular expressions for TIMESTAMP
use constant _TEST_TIMESTAMPS => (        # from "6.2.3.1.  Examples"
        '1985-04-12T23:20:50.52Z' => 1,
        '1985-04-12T19:20:50.52-04:00' => 1,
        '2003-10-11T22:14:15.003Z' => 1,
        '2003-08-24T05:14:15.000003-07:00' => 1,
        '2003-08-24T05:14:15.000000003-07:00' => 0,        # invalid!
        '-' => 1                        # NILVALUE
);

UNITCHECK
{

    # from "6.2.3.1.  Examples"
    while (my ($str, $valid) = each _TEST_TIMESTAMPS) {
        if ($valid) {
            die "timestamp ($str) is not valid\n"
                if ($str !~ /^${RE_timestamp}$/o);
        } else {
            die "timestamp ($str) is valid\n"
                if ($str =~ /^${RE_timestamp}$/o);
        }
    }
}

最重要的是我在 Perl 5.18.2 中遇到了错误,但是当我在另一台使用 Perl 5.26.1 的机器上检查时,根本没有错误消息!

那么我也可以让代码与旧的 Perl 5.18 一起工作吗?

丑陋的解决方法

实验我发现 each (my %h = _TEST_TIMESTAMPS) 没有帮助,但是当使用 my %h = _TEST_TIMESTAMPS; 然后 each %h 时,错误就消失了。

我仍然不明白发生了什么(在使用常量之前,我在 UNITCHECK 中使用了本地 my 哈希。 显然我想改用包级常量。

_TEST_TIMESTAMPS 作为“list constant" in the question and will behave only as a flat list, not as a hash. See 给出了非常详细的讨论。此外,它被 each 拒绝,因为错误消息告诉我们。

可以改用 hash-reference

use constant _TEST_TIMESTAMPS => { ... };

each 接受了这一点。它似乎适用于问题的片段,但我会小心使用更多相关内容。在 constant 中使用引用有其自身的问题。

此外,请记住 constant pragma 中的对象实际上是子例程( 在当前实现中 ”,如文档所述);参见 Technical NoteBugs。这会影响人们对他们在各种情况下的行为的期望。

另一方面,将 const 换成 Const::Fast,使其在所有情况下都能正常工作,使用正常的词法变量

use warnings;
use strict;
use feature 'say';

use Const::Fast;

our $RE_timestamp;    
BEGIN { our $RE_timestamp = qr/whatever/; }

# check Regular expressions for TIMESTAMP
our %_TEST_TIMESTAMPS;
BEGIN {
    const our %_TEST_TIMESTAMPS => (        # from "6.2.3.1.  Examples"
        '1985-04-12T23:20:50.52Z' => 1,
        '1985-04-12T19:20:50.52-04:00' => 1,
        '2003-10-11T22:14:15.003Z' => 1,
        '2003-08-24T05:14:15.000003-07:00' => 1,
        '2003-08-24T05:14:15.000000003-07:00' => 0,        # invalid!
        '-' => 1                        # NILVALUE
    );  
}

UNITCHECK
{
    # from "6.2.3.1.  Examples"
    while (my ($str, $valid) = each %_TEST_TIMESTAMPS) {
        if ($valid) {
            die "timestamp ($str) is not valid\n"
                if ($str !~ /^${RE_timestamp}$/o);
        } else {
            die "timestamp ($str) is valid\n"
                if ($str =~ /^${RE_timestamp}$/o);
        }
    }
}

Const::Fast 引入的词法必须在声明时赋值(当然以后不能重新赋值),所以这里必须是一个 our 变量,因为它需要在 BEGIN 块但在其外部声明,因此要设置 UNITCHECK 可见。

我用Const::Fast merely as my preference; another viable library is Readonly.

请注意,如果要使用 strict,则必须首先在任何块之外按词法声明 $RE_timestamp(为什么不使用它呢?)。我纠正了这一点。它不需要 our 对于任何一个,但我保留它,因为可能还有其他原因。

至于为什么这在后来的 Perls 中不是问题,我想它是 hashref 或 arrayref 的要求在某个时候被取消了。 (我现在无法检查。)


我们可以在多个地方正式声明一个变量是全局变量词法别名的属性,our.

问题好像是each坚持一个散列变量(不是常量),所以我就这样解决了(不是正版,但是可以) :

UNITCHECK
{
    my $hr = { _TEST_TIMESTAMPS };
    while (my ($str, $valid) = each %$hr) {
        #...
    }
}

但是 { _TEST_TIMESTAMPS } 将创建一个新的临时散列,因此最好将散列引用分配给常量。 那么你仍然需要一个变量,但不会创建临时哈希对象。