我正在尝试编写一个接收数组引用的子例程,然后删除该数组的一些元素。例如:
use strict;
use warnings;
my @a = (1, 2, 3, 6);
func1 (\@a);
sub func1 {
my $a = shift;
my @b = (2, 6);
for my $val_to_remove (@b) {
for my $i (0..$#$a) {
my $val = $a->[$i];
if ( $val == $val_to_remove ) {
splice @$a, $i, 1;
last;
}
}
}
}
至少可以说,使用两个for
循环似乎有些尴尬。
有可能简化这个吗?
我也试过
use strict;
use warnings;
my @a = (1, 2, 3, 6);
my $temp = \@a;
func2 (\$temp );
sub func2 {
my $a = shift;
$$a = [2, 6];
}
但后来@a
未被修改,而$temp
将是..
我还想避免传递对引用的引用,因为这会搞乱其他模块的调用语法。
答案 0 :(得分:2)
使用哈希作为指标函数,有效识别要删除的项目;使用grep过滤它们:
sub func1 {
my $a = shift;
my %b = map { ($_ => 1) } (2, 6);
@$a = grep { !$b{$_} } @$a;
}
答案 1 :(得分:1)
Loic's solution运作良好且非常易读。除非你使用大型数组导致grep
占用大量内存,或者性能绝对至关重要,否则我会推荐它。
使用splice
:
use strict;
use warnings;
use Data::Dump;
my @haystack = (1, 2, 3, 6);
my %needle = map { $_ => 1 } (2, 6);
foreach my $i (reverse 0 .. $#haystack) {
splice @haystack, $i, 1 if exists $needle{ $haystack[$i] };
}
dd \@haystack;
[1, 3]
请注意,必须以相反的顺序迭代@haystack
,因为每次删除元素时,其余元素都会向左移动,从而更改数组索引。
以下是针对PerlMonks上的corrected benchmark编写的BrowserUk foreach array - delete current row ?的略微修改版本的结果。原始基准测试包括从阵列中删除元素的其他几种方法,为简单起见,我将其遗漏了。
$ ./benchmark -N=1e2
Rate grep for_splice
grep 40959/s -- -37%
for_splice 65164/s 59% --
$ ./benchmark -N=1e3
Rate grep for_splice
grep 4072/s -- -38%
for_splice 6515/s 60% --
$ ./benchmark -N=1e4
Rate grep for_splice
grep 366/s -- -33%
for_splice 550/s 50% --
$ ./benchmark -N=1e5
Rate grep for_splice
grep 32.7/s -- -38%
for_splice 52.9/s 62% --
$ ./benchmark -N=1e6
(warning: too few iterations for a reliable count)
Rate grep for_splice
grep 2.36/s -- -28%
for_splice 3.28/s 39% --
基准代码本身:
#!/usr/bin/perl -sl
use strict;
use warnings;
use Benchmark 'cmpthese';
our $N //= 1e3;
our $I //= -1;
# 10% the size of the haystack
my $num_needles = int($N / 10) || 1;
our @as;
@{ $as[ $_ ] } = 1 .. $N for 0 .. 4;
our %needle = map { int(rand($N)) => 1 } 1 .. $num_needles;
cmpthese $I, {
for_splice => q[
my $ar = $as[0];
foreach my $i (reverse 0 .. $#$ar) {
splice @$ar, $i, 1 if exists $needle{ $ar->[$i] };
}
$I == 1 and print "0: ", "@$ar";
],
grep => q[
my $ar = $as[1];
@$ar = grep { ! exists $needle{$_} } @$ar;
$I == 1 and print "1: ", "@$ar";
],
};
答案 2 :(得分:1)
如果您还要修改数组的内容,则不能使用简单的for (LIST)
循环来迭代数组的索引。这是因为最后一项的索引可能会改变,如果删除当前元素并递增计数器,您也将跳过元素。
需要while
循环,或等效的C风格for
。
此程序演示以及uing List::Util::any
以检查是否应删除数组元素
use strict;
use warnings;
use List::Util 'any';
my @a = (1, 2, 3, 6);
func1 (\@a);
use Data::Dump;
dd \@a;
sub func1 {
my ($a) = @_;
my @b = (2, 6);
for ( my $i = 0; $i < @$a; ) {
if ( any { $a->[$i] == $_ } @b ) {
splice @$a, $i, 1;
}
else {
++$i;
}
}
}
<强>输出强>
[1, 3]
答案 3 :(得分:0)
对于这种性质的问题,通常更容易构建所需的数组,而不是从现有数组中删除。
my @a = ( 1, 2, 3, 6 );
sub func1 {
my $aref = shift @_;
my @b = ( 2, 6 );
my @results = ();
for my $item ( @$aref ){
if( grep { $item == $_ } @b ){
next;
}
push @results, $item;
}
return @results;
}
my @results = func1( \@a );
say "@results";