如何在Perl中缩小一个数组?

15
我该如何在Perl中缩短一个数组?我阅读了一些网页,指示我可以这样赋值:
$#ARRAY = 42;

我看到使用$#已经被弃用了。我需要一个能够适用于数组的解决方案,但这个方法并不行:

$#$ARRAY[$i] = 42;

$#已被弃用,但$#不等同于$#数组。我没有在任何地方读到$#数组已被弃用,尽管它令人困惑,我建议不要使用它。 - Leon Timmermans
8个回答

20

我不知道指定$#ARRAY已经被弃用了;从5.10.0开始,perldoc perldata肯定没有关于它的说明。这是截断数组的最快方法。

如果你想要更易读的方法,请使用splice

splice @ARRAY, 43;

(请注意使用43而不是42 - $#ARRAY会获取数组的最后一个索引,而splice则需要数组的长度作为参数)。

至于处理嵌套数组,我猜你是指能够通过引用截断嵌套数组吗?在这种情况下,你想要:

$#{$ARRAY->[7]} = 42;
或者
splice @{$ARRAY->[7]}, 43;

起初我读成了“42 - $#ARRAY”。重新阅读后,我意识到你的意思是:“注意,在这里使用43而不是42。$#ARRAY会得到...”。 - Kyle

10
您的选择几乎没有限制(我在这里概述了五种方法),但是您的策略将受到您具体需求和目标的支配。(所有示例都将@array转换为不超过$N个元素)

[编辑]

正如其他人指出的那样,建议使用原始问题中提出的方式实际上并未过时,它提供了最快,最简洁但不一定是最易读的解决方案。它还会导致由少于$N个元素组成的数组扩展为空元素

$#array = $N-1;

最少的代码:

#best for trimming down large arrays into small arrays
@array = $array[0..($N-1)];

在从大数组中删除少量元素时,最高效的方法:

#This is a little less expensive and clearer
splice(@array, $n, @#array);

除非你真的喜欢delete(),否则在几乎所有情况下都不建议使用:

#this is the worst solution yet because it requires resizing after the delete
while($N-1 < $#array)
{
   delete(array[$i]);
}

如果需要将列表的剩余部分按相反顺序排列,则此功能非常有用:

#this is better than deleting because there is no resize
while($N-1 < $#array)
{
    pop @array;
    #or, "push $array2, pop @array;" for the reverse order remainder
}

有助于长期节省时间:

#don't put more values into the array than you actually want

这在处理大量元素但只删除少量元素的数组时会非常糟糕。 - Leon Timmermans
好的观点。我扩展了原始答案,包括那个警告并提供了其他几个选项。 - Frosty

7

5

您基本上已经给出了标准答案。通过设置最后一个索引,可以缩短数组:

$#Array = 42

用$#Foo表示数组中的最后一个索引绝对不会被弃用。同样,对其进行赋值也不会被弃用。引用perldata文档:

数组的长度是标量值。您可以通过评估$#days来查找数组@days的长度,就像在csh中一样。但是,这不是数组的长度;它是最后一个元素的下标,因为通常有第0个元素。 将$#days分配给实际上会改变数组的长度。以这种方式缩短数组会破坏中间值。延长先前缩短的数组不会恢复那些元素中的值。(在Perl 4中曾经这样做,但我们必须打破这个规则,以确保在预期时调用析构函数。)


2
  • $#array is the last index of the array.
  • $#$array would be the last index of an array pointed at by $array.
  • $#$array[$i] means you're trying to index a scalar--can't be done. $#{$array[3]} properly resolves the subscripting of the main array before we try to reference the last index.
  • Used alone

    $#{$array[3]} = 9;

    assigns a length of 9 to the autovivified array at $array[3].

  • When in doubt, use Data::Dumper:

    use Data::Dumper;
    $#{$array[3]} = 5;
    $#array       = 10;
    print Dumper( \@array, $array ), "\n";
    

0

你可以这样做

splice @array, $length;
#or
splice @{$arrays[$i]}, $length;

您的第二个示例中有一个错误的点。 - Michael Carman

0

这个问题有两种解释方式。

  • 如何减少数组的长度?
  • 如何减少数组所占用的内存量?

到目前为止,大多数答案都集中在前者。在我看来,最好的答案是使用 splice 函数。例如,要从末尾删除10个元素:

splice @array, -10;

然而,由于Perl如何管理数组的内存,确保数组占用更少的内存的唯一方法是将其复制到一个新数组中(并让旧数组的内存被回收)。为此,我倾向于考虑使用切片操作。例如,要删除10个元素:
@new = @old[ 0 .. $#old - 10 ]

这里是一个针对包含500个元素的数组(使用2104字节)的不同方法的比较:

  original: length  500 => size 2104
     pound: length  490 => size 2208
    splice: length  490 => size 2104
    delete: length  490 => size 2104
     slice: length  490 => size 2064

你可以看到,只有切片操作(复制到新数组)的大小比原始数组小。

这是我用于此分析的代码:

use strict;
use warnings;
use 5.010;
use Devel::Size qw/size/;

my @original = (1 .. 500);
show( 'original', \@original );

my @pound = @original;
$#pound = $#pound - 10;
show( 'pound', \@pound );

my @splice = @original;
splice(@splice,-10);
show( 'splice', \@splice);

my @delete = @original;
delete @delete[ -10 .. -1 ];
show( 'delete', \@delete );

my @slice = @original[0 .. $#original - 10];
show( 'slice', \@slice);

sub show {
    my ($name, $ref) = @_;
    printf( "%10s: length %4d => size %d\n", $name, scalar @$ref, size($ref));
}

0

$#{$ARRAY[$i]} = 42;


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