正确检测 Perl 子例程参数是否为数组

Properly detecting if Perl subroutine argument is array

我正在尝试将库从 Javascript 移植到 Perl,但在检测数组参数时遇到一些问题。

精简示例(在 Javascript 中)看起来像这样:

const say = console.log;

function test(array, integer)
{
 if(Array.isArray(array))
 {
  say("First argument is array");
 }
 else
 {
  say("First argument is not array");
  array = [array];
 }
 for(let index = 0; index < array.length; ++index)
 {
  say(array[index]);
 }
 say("Second argument: ", integer);
 say("");
}

var a = ["foo", "bar"];
var t = "baz";
var i = 1024;

say("Testing on array");
test(a, i);

say("Testing on string");
test(t, i);

输出:

Testing on array
First argument is array
foo
bar
Second argument:  1024

Testing on string
First argument is not array
baz
Second argument:  1024

这是 Perl 版本:

use feature qw(say);

sub test 
{
 my ($text, $integer ) = @_;
 my (@array) = @_;  
 if(ref(@array) == "ARRAY") 
 {
  say("First argument is array");
 }
 else 
 {
  say("First argument is not array");
  @array = ($text);
 }
 for my $index (0 .. $#array)
 {
  say($array[$index]);
 }
 say("Second argument: ", $integer);     
}

my @a = ("foo", "bar");
my $t = "baz";
my $i = 1024;

say("Testing on array");
test(@a, $i);

say("Testing on string");
test($t, $i);

输出:

Testing on array
First argument is array
foo
bar
1024
Second argument: bar

Testing on string
First argument is array
baz
1024
Second argument: 1024

我也尝试过许多其他方法,例如在数组名称前加上反斜杠等等,但都无济于事。我相当确定这在 Perl 中一定是可能的。或者这可能是语言本身的某种限制?

Perl 子例程不将数组作为参数,仅将标量作为参数。子调用中的数组折叠成标量列表。如果你想传递一个数组,你有两个选择。

  1. 传递数组引用,例如test(\@a, $i),或 test([ @a ], $i)。或者更好的是,首先传递标量参数,然后将其余参数放入数组中。
  2. 深入研究 Perl 原型并了解它们的工作原理。

我建议解决方案 1。通常你可以这样做

test($i, @a);

sub test {
    my $i = shift;
    my @a = @_;
    ....
}

test($i, [ @a ]);

sub test {
    my $i = shift;
    my $aref = shift;
    my @a = @$aref;       # expand to normal array (not strictly needed)
    ....
}

请注意,传递对数组的引用 @a 将允许 sub 更改数组中的值,因此将其作为匿名数组传递 [ @a ] 更安全。

另外,代码:

my @a;
print ref(\@a);

将打印 ARRAY,但 ref(@a) 将不打印任何内容。

此外,如果您使用过

use strict;
use warnings;

你会得到非常有启发性的警告

Argument "ARRAY" isn't numeric in numeric eq (==) 

这告诉你在字符串比较中,你应该使用 eq 而不是 == 这是数字。因为 Perl 会默默地将空字符串转换为数字上下文中的 0,并将字符串 ARRAY 转换为 0,比较结果为真,这导致您比较 return 误报。

切勿在没有 strictwarnings 的情况下编写 Perl 代码。