在Perl中计算稀疏数组中的元素数量

28

如何获取数组中的所有项数量,而不是最后一个 id?

我找到的两种方法都不起作用:

my @a;
# Add some elements (no consecutive ids)
$a[0]= '1';
$a[5]= '2';
$a[23]= '3';

print $#a, "\n"; # Prints 23
print scalar(@a), "\n"; # Prints 24

我期望得到3个结果,但实际上只有2个。 我该如何解决这个问题?

6个回答

40

编辑:哈希表与数组

正如cincodenada在评论中正确指出的,ysth给出了更好的答案:我应该用另一个问题回答你的问题:“你真的想使用Perl数组吗?哈希表可能更合适。”

数组会为迄今使用的最大索引分配所有可能的内存。在您的示例中,您分配了24个单元格(但仅使用了3个)。相比之下,哈希表仅为实际使用的字段分配空间。

数组解决方案:标量grep

以下是两种可能的解决方案(请参见下文解释):

print scalar(grep {defined $_} @a), "\n";  # prints 3
print scalar(grep $_, @a), "\n";            # prints 3
解释:在添加了 $a[23] 后,你的数组实际上包含了 24 个元素——但大多数未定义(也可以理解为假)。你可以计算已定义元素的数量(第一种解决方案所做的),也可以计算真实元素的数量(第二种解决方案)。
区别在哪里?如果你设置 $a[10]=0,则第一种解决方案将计数,但第二种解决方案将不计数(因为 0 是假但已定义)。如果你设置 $a[3]=undef,则两种解决方案都不会计数。
哈希表解决方案(由 yst 提供)如另一个解决方案所建议的,你可以使用哈希表来避免所有这些问题。
$a{0}  = 1;
$a{5}  = 2;
$a{23} = 3;
print scalar(keys %a), "\n";  # prints 3

这个解决方案计算零和未定义的值。


1
这个答案的最后一部分是正确的。grillix似乎来自PHP背景。PHP所谓的“数组”实际上更类似于Perl的哈希表,在这种情况下应该使用后者。 - cincodenada
这个想法如何在Perl中扩展到多维数组? - damned
修正 grep 格式的小问题: print scalar(grep { $_ } @a), "\n"; - arikin
@arikin:我添加了一个逗号来解决语法问题。感谢你指出! - Yaakov Belch
在哈希解决方案中,你可以安全地省略keys调用:简单的scalar %a就足够了。 - cl0ne

16

看起来你需要一个稀疏数组。正常的数组中有24个项目,但稀疏数组只有3个。在Perl中,我们使用哈希表来模拟稀疏数组:

#!/usr/bin/perl

use strict;
use warnings;

my %sparse;

@sparse{0, 5, 23} = (1 .. 3);

print "there are ", scalar keys %sparse, " items in the sparse array\n",
    map { "\t$sparse{$_}\n" } sort { $a <=> $b } keys %sparse;

keys函数在标量上下文中返回稀疏数组中项目的数量。使用哈希模拟稀疏数组的唯一缺点是,如果它们的顺序很重要,则必须在迭代之前对键进行排序。

您还必须记住使用delete函数从稀疏数组中删除项目(仅将其值设置为undef不足够)。


这是正确的。然而,Tie::IxHash是可选的;在你的例子中似乎是不必要的。 另外,不需要提供谓词来排序,因为那是默认值。“sort keys %sparse”同样有效。 - spoulson
糟糕,Tie::IxHash是从另一个示例中留下来的,让我将其删除。 - Chas. Owens
2
@Spoulson 不,默認排序是按字母順序而非數字大小,因此鍵(1、2和10)將被排序為(1、10、2)。 - Chas. Owens

14

也许你想要一个哈希(Hash)代替数组,或者同时使用。数组是一组有序的元素;如果你创建 $foo[23],那么你就隐式地创建了 $foo[0]$foo[22]


8
print scalar grep { defined $_ } @a;

6
说明:perl实际上没有像grilix想要的“稀疏”数组。如果你说“my @a; $a[10]=5;”,那么perl会创建一个有11个条目的数组:前10个填充为'undef',第11个填充为'5'。而“scalar @a”和“$#a”报告的总是整体长度/最后一个索引。kcwu过滤数组以仅计算已定义的条目数。 - user55400
1
它能用,但不是很好。grep函数是O(n)的,这意味着如果你有@a[1, 1_000_000] = (1, 2); 那么它必须查看每一个1,000,000个项目才能得到计数,这也意味着你会占用大量内存并且没有理由使用哈希代替。 - Chas. Owens
呀..如果我必须使用数组,它可以工作,但我认为我可以用哈希代替。无论如何,他只是回答了我所询问的。谢谢大家。 - grilix
你能否在你的回答中添加解释?但是请不要包含“编辑:”,“更新:”或类似的内容 - 回答应该看起来像是今天写的。 - Peter Mortensen

1
@people = qw( bob john linda ); 
$n = @people; # The number 3
Print " The number in the list is $n \n"; 

Perl中的表达式总是根据它们所处的上下文返回适当的值。
例如,对于数组的“name” *,在列表上下文中,它会给出元素列表。但在标量上下文中,它返回数组中元素的数量。

0
sub uniq {
    return keys %{{ map { $_ => 1 } @_ }};
}
my @my_array = ("a","a","b","b","c");
#print join(" ", @my_array), "\n";
my $a = join(" ", uniq(@my_array));
my @b = split(/ /,$a);
my $count = $#b;

2
这段代码存在严重问题。首先,如果数组中的项包含空格,则会出现错误。其次,它会对整个数组项集合进行一次迭代,并对定义的项集合进行两次迭代。第三,像所有基于数组的解决方案一样,它无法区分用户设置的未定义值与数组中的空槽(根据代码的使用方式,这可能不是坏事)。 - Chas. Owens

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