在Perl中查找数组的大小

279

我似乎遇到了几种不同的方法来查找数组的大小。这三种方法之间有什么区别?

my @arr = (2);
print scalar @arr; # First way to print array size

print $#arr; # Second way to print array size

my $arrSize = @arr;
print $arrSize; # Third way to print array size

13
其他方法:print 0 + @arrprint "".@arrprint ~~@arr
  • print 0 + @arr 将数组 @arr 强制转换为数字并将其与零相加,然后输出结果。
  • print "".@arr 使用字符串连接操作符将 @arr 数组中的元素连接成一个字符串,并打印结果。
  • print ~~@arr 将数组 @arr 强制转换为布尔值,然后再次强制将其转换为数字。这会将空数组转换为 0,非空数组转换为 1,并输出结果。
- mob
4
有人可能希望避免使用"".@arr,因为"@arr"会产生完全不同的效果。 - ikegami
44
“第二种方法”并不是打印数组大小的方法... - tadmc
在标量上下文中,@arr 返回表格大小。$x=@arr 就是标量上下文。$#arr 返回数组的最后一个索引。索引从 0 开始计数,那么等式 $#arr+1 == @arr 是正确的。如果你按顺序写一些元素,例如 $arr[100]='any',则表格会自动增加到最大索引 100,并包括索引 0 在内,总共有 101 个元素。 - Znik
这个问题在搜索引擎结果中出现,但似乎很不可能没有一个更早的问题,来自 Stack Overflow 存在的前两年。有哪些候选者? - Peter Mortensen
候选项:Perl 中的$#array是什么意思?(2008年10月27日) - Peter Mortensen
12个回答

265
第一种和第三种方法相同:它们在标量上下文中评估数组。我认为这是获得数组大小的标准方式。
第二种方法实际上返回数组的最后一个索引,这通常与数组大小不同。

32
(1,2,3) 的大小为 3,索引(默认情况下)为 0,1 和 2。因此,在这种情况下,$#arr 将是 2,而不是 3。 - Nate C-K
5
预定义变量 $[ 指定了“数组中第一个元素的索引和子字符串中第一个字符的索引”(perldoc perlvar)。它默认设置为0,强烈不建议将其设置为非0值。 - Keith Thompson
6
@Keith Thompson,“$[”已经被弃用了(已有十年左右)。现在使用“$[”会引发弃用警告,即使您没有打开警告功能。在5.16版本中,将任何非零值分配给“$[”都将导致错误。我们能不能不再提到“$[”了? - ikegami
2
@Keith Thompson,比5.14版本还要旧。但是像我说的那样,它已经被强烈反对和废弃的时间比那还要长得多,并且使用“$[`”的人会了解它的影响。 - ikegami
9
是的,但是想要理解 scalar @arr$#arr 之间差异的人仍然应该了解 $[ 的可能影响,即使这种情况很少见。 - Keith Thompson
显示剩余3条评论

49

首先,第二个表达式 ($#array) 和另外两个表达式不等同。 $#array 返回数组的最后一个索引,它比数组大小少一。

另外两个表达式 (scalar @arr$arrSize = @arr) 实际上是相同的。你只是使用了两种不同的方法来创建标量上下文。这归结为可读性的问题。

我个人更喜欢以下方式:

say 0+@array;          # Represent @array as a number

我认为这比较清晰易懂。
say scalar(@array);    # Represent @array as a scalar

并且

my $size = @array;
say $size;

单独看这个代码,后者看起来很清晰,但是当它与其他代码组合时,我发现多余的行会降低其清晰度。它对于教授在标量上下文中使用@array非常有用,也许如果您想多次使用$size


18
个人而言,我更喜欢使用“scalar”关键字的版本,因为它可以明确地强制使用标量上下文。my $size=@array看起来可能是一种错误,其中使用了错误的符号。 - Nate C-K
5
这真是个糟糕的想法。没有必要使用“scalar”的人会学到错误的功课。他们开始认为运算符返回可以强制转换为标量的列表。我已经见过这种情况很多次了。 - ikegami
2
为什么这 "没有理由"?因为你正在使用 scalar,所以你正在将列表强制转换为标量环境。这是正确使用它的原因。你的示例完全做了同样的事情,但依赖于在隐式标量环境中评估列表变量时 Perl 的行为。因此,你的示例要求读者了解 Perl 在该上下文中的隐式行为。你只是向表达式添加了一层隐式行为,并且 Perl 已经有太多的隐式行为,你必须通过推理才能解码程序。 - Nate C-K
2
@Nate C-K,关于“为什么没有原因?你正在使用scalar是因为你正在将列表强制转换为标量上下文”,你证明了我学习错误教训的观点。这是完全错误的。 scalar 永远不会强制转换任何列表。(如果它这样做了,scalar(@array)scalar(@array[0..$#array]) 将返回相同的结果。)scalar(@array) 告诉 @array 返回一个标量,而你已经用 my $size= 告诉它要这样做了。 - ikegami
2
信不信由你,开发人员必须调试其他开发人员编写的代码。而且开发人员必须调试三年前自己编写的代码。 - Nate C-K
显示剩余8条评论

32

通过将数组强制转换为标量上下文来获取其大小,此时它会被计算为其大小:

print scalar @arr;

这是将数组强制转换为标量上下文的另一种方式,因为它被分配给了一个标量变量:

my $arrSize = @arr;

这将获取数组中最后一个元素的索引,因此实际上是大小减1(假设索引从0开始,尽管在Perl中可以调整,但通常不是一个好主意):

print $#arr;

这最后一个方法不太适合用于获取数组大小。如果您只想获取数组的最后一个元素,则此方法会很有用:

my $lastElement = $arr[$#arr];

同时,正如你可以在Stack Overflow上看到的那样,大多数语法高亮显示器无法正确处理这个结构...


2
一个旁注:只需使用$arr[-1]来获取最后一个元素。使用$arr[-2]来获取倒数第二个元素,以此类推。 - tuomassalo
1
@tuomassalo:我同意你的建议是更好的方法。回想起来,“$#arr”不是一个非常有用的功能,其他语言没有它也不是偶然的。 - Nate C-K

8

使用第二种方法,添加1:

print $#arr + 1; # Second way to print array size

for [0..$#array] { print $array[$_ ] } 这段代码非常适合用于遍历数组。它的优点在于,你可以同时得到元素和计数器,并且它们是对齐的。 - Westrock

6

如果我们稍微修改第二个表达式,所有三个表达式都会得到同样的结果:

my @arr = (2, 4, 8, 10);

print "First result:\n";
print scalar @arr; 

print "\n\nSecond result:\n";
print $#arr + 1; # Shift numeration with +1 as it shows last index that starts with 0.

print "\n\nThird result:\n";
my $arrSize = @arr;
print $arrSize;

6
这跟这个回答和这个回答提到的内容有什么不同吗? - devnull

5

例子:

my @a = (undef, undef);
my $size = @a;

warn "Size: " . $#a;   # Size: 1. It's not the size
warn "Size: " . $size; # Size: 2

3

“Perl变量类型”部分perlintro文档包含了

The special variable $#array tells you the index of the last element of an array:

print $mixed[$#mixed];       # last element, prints 1.23

You might be tempted to use $#array + 1 to tell you how many items there are in an array. Don’t bother. As it happens, using @array where Perl expects to find a scalar value (“in scalar context”) will give you the number of elements in the array:

if (@animals < 5) { ... }

perldata文档也在“标量值”部分中涵盖了这一点。

If you evaluate an array in scalar context, it returns the length of the array. (Note that this is not true of lists, which return the last value, like the C comma operator, nor of built-in functions, which return whatever they feel like returning.) The following is always true:

scalar(@whatever) == $#whatever + 1;

Some programmers choose to use an explicit conversion so as to leave nothing to doubt:

$element_count = scalar(@whatever);

在同一部分之前的文档中介绍了如何获取数组的最后一个元素的索引。

数组的长度是一个标量值。您可以通过计算csh中的$#days来找到数组@days的长度。但是,这不是数组的长度;它是最后一个元素的下标,因为通常有一个0th元素。


3

以下内容来自于perldata,应该是安全的引用:

The following is always true:

scalar(@whatever) == $#whatever + 1;

只要你不使用$#whatever++这样的语法并神秘地增加数组大小。

数组索引从0开始。

以及

You can truncate an array down to nothing by assigning the null list () to it. The following are equivalent:

    @whatever = ();
    $#whatever = -1;

这让我想到了我正在寻找的内容,即如何检测数组是否为空。我发现可以使用 $#empty == -1 进行判断。


3

有多种方法可以打印数组的大小。以下是所有用法的含义:

假设我们的数组为my @arr = (3,4);

方法1:scalar

这是获取数组大小的正确方法。

print scalar @arr;  # Prints size, here 2

方法二:索引号

$#arr 可以得到数组的最后一个索引号。因此,如果数组大小为10,则其最后一个索引号为9。

print $#arr;     # Prints 1, as last index is 1
print $#arr + 1; # Adds 1 to the last index to get the array size

我们在这里加上1,考虑到数组是从0开始索引的。但是,如果它不是以零为基础的,那么这个逻辑将会失败。
perl -le 'local $[ = 4; my @arr = (3, 4); print $#arr + 1;'   # prints 6

上面的例子会打印出6,因为我们将其初始索引设置为4。现在索引将是5和6,分别带有元素3和4。
方法3: 当数组在标量上下文中使用时,它返回数组的大小。
my $size = @arr;
print $size;   # Prints size, here 2

实际上,方法三和方法一是相同的。


1

使用int(@array),因为它将参数视为标量。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接