在Perl中,排除数组元素的最佳方法是什么?

5

我使用以下代码在@{$x}中排除索引为@{$index}的元素。但是我不确定这是否是实现此功能的最有效方法。是否有更好的方法?

sub arrayexclude {
    my $x = shift;
    my $index = shift;

    my @keep_index = (0 .. (scalar(@{$x})-1));

    delete @keep_index[@{$index}];

    my $result=[];

    for my $i (@keep_index) {
        if(defined $i) {
            push @{$result}, @{$x}[$i];
        }
    }
    return $result;
}

1
作为附注,0 .. (scalar(@{$x})-1) 更容易写成 0 .. $#$x,或者如果你喜欢的话是 0 .. $#{$x} - hobbs
"$#foo"经常被误用,如果你在给它加1,那么你做错了...但是如果你正在遍历数组的索引或者谈论最后一个索引,那么它恰好就是你想要的东西。 - hobbs
3个回答

2
你不需要除了数组切片之外的任何东西,因此我建议避免为此创建一个子程序。
my @wanted = @array[@indices];

如果您正在使用数组引用,则同样适用相同的方法:

my @wanted = @{$array}[@$indices];

我认为 OP 需要在这些索引处 排除 元素。 - zdim

1
准备哈希表以便高效地识别索引,然后使用它来索引原始数组。
my %ref_index;
@ref_index{ @ind_toss } = ();    
@arr_filt = @arr_orig[ grep { !exists $ref_index{$_} } (0..$#arr_orig) ];

最终的@arr_filt包含了除@ind_toss以外的@arr_orig中的元素。

请参考ysththis post中提供的解决方案,以便通用地通过另一个数组过滤数组元素。


将它包装在一个子程序中并运行。要排除的索引数组为@ind_toss,原始数组为@arr_orig

use warnings;
use strict;

my @ind_toss = (1, 4, 5);
my @arr_orig = ('a', '1', 'b', 'c', '2', '6', 'd', 'e');

my @filtered = @{ filter_array_by_index(\@arr_orig, \@ind_toss) };

print "@filtered" . "\n";    

sub filter_array_by_index {
    my ($rarr, $rind) = @_;    
    my %ref_index;
    @ref_index{ @$rind } = (); 
    return [ @$rarr[grep { !exists $ref_index{$_} } (0..$#$rarr)] ];
}

打印

a b c d e

注释

Oleg V. Volkov的评论中所示,子程序中的return也可以写成:

return [ map { !exists $ref_index{$_} ? $rarr->[$_] : ()  } (0..$#$rarr) ];

这避免了通过grep和切片构建列表,而是有条件地索引到数组中。

实际上,这会打印出 6 - ChatterOne
@ChatterOne 已修复,非常感谢。这是来自不同版本的一个错别字 :( - zdim
为什么要使用 grep + 切片,如果你可以直接使用 map 并且摆脱临时索引数组呢? - Oleg V. Volkov

0

只是稍微整理一下zdim的答案(无论如何都是正确的):

sub filter_array_by_index {
    my ( $rarr, $rind ) = @_;
    my %ref_index = map { $_ => 1 } @ind_toss;
    my @indices = grep { !$ref_index{$_} } (0..$#$rarr);

    return @{$rarr}[@indices];
}

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