简体   繁体   English

如何删除引用数组的元素?

[英]How can I delete an element of a referenced array?

I want to remove elements from a few large arrays with a subroutine. 我想用一个子程序从几个大数组中删除元素。 I use a reference to avoid a copy into the sub. 我使用引用来避免副本进入子。

@a=qw(ok now what is hi the matter);

sub zonk {
  $array=shift; # this is a reference of an array
  foreach $i (0..$#$array) { # I saw some say to avoid last element to get size
    #if (@$array[$i] =~ /hi/) { delete @$array[$i]; }
    #if ($array->[$i] =~ /hi/) { delete $array->[$i]; }
    #if ($array->[$i] =~ /hi/) { delete @$array->[$i]; }
    if ($array->[$i] =~ /hi/) { print "FOUND "; }
    print $array->[$i],"\n";
  }
  @$array = grep{$_} @$array; # removes empty elements
}
zonk(\@a);
print join(':',@a);

If I run the program above as is I get: 如果我按原样运行上面的程序:

ok
now
what
is
FOUND hi
the
matter
ok:now:what:is:hi:the:matter

But if I use any of the commented lines instead I get: 但是,如果我使用任何注释行,我得到:

delete argument is not a HASH element or slice at hi.pl line 10. delete参数不是hi.pl第10行的HASH元素或切片。

I tried splice originally but then the indices were shifting and confused the iteration. 我最初尝试拼接,但随后指数正在转移并混淆了迭代。 Would be nice to know all the methods mentioned in this post, however the most efficient is what I am looking for :) 很高兴知道这篇文章中提到的所有方法,但最有效的是我正在寻找:)

Addendum: This works perfectly (I mean every commented line) on my linux machine (ubuntu 9.10, perl 5.10) but the above error is on my Windows 7 box at work using perl 5.005_03. 附录:这在我的linux机器上是完美的(我的意思是每条注释行)(ubuntu 9.10,perl 5.10),但上面的错误是在工作时使用perl 5.005_03在我的Windows 7盒子上。 Upgrading is not an option. 升级不是一种选择。

Thanks 谢谢

Why not grep from the get-go? 为什么不从一开始就贪图?

@array = grep { !/hi/ } @array;
# Or, for *referenced* array
@$arrayRef = grep { !/hi/ } @$arrayRef;

A small set of notes to clarify questions that arose in comments: 一小组注释,用于澄清评论中出现的问题:

  1. This method (or any method using grep including original poster's code) will increase the memory usage of the script by the size of the newly resultant array. 此方法(或使用grep包括原始海报代码的任何方法) 通过新结果数组的大小增加脚本的内存使用量。

    Eg if the script (sans the first array) took up 10MB of memory, the original array took 15MB of memory, and the resulting array took 14MB of memory, then the total memory footprint of your program will increase from 25MB to 39MB while grep is running. 例如,如果脚本(没有第一个阵列)占用10MB内存,原始阵列占用15MB内存,结果阵列需要14MB内存,那么程序的总内存占用将从25MB增加到39MB,而grep是运行。

  2. Once the grep comlpetes, the memory used by the original array will become available for garbage collection (with some caveats irrelevant to this post). 一旦grep comlpetes,原始数组使用的内存将可用于垃圾收集(有一些警告与此帖无关)。

  3. However - and this is important - even if the original 15MB of data are garbage collected, that 15MB will not be returned by Perl to operating system - eg the script's memory footprint will remain 39MB and won't drop to 24MB even after garbage collection. 但是 - 这很重要 - 即使最初的15MB数据是垃圾收集的, Perl也不会将15MB返回到操作系统 - 例如,脚本的内存占用量将保持在39MB ,即使在收集垃圾后也不会降至24MB。

  4. On the good side, that freed-up 15MB will be available for memory allocation throughout the rest of your program's lifetime (leaving aside memory fragmentation issues) - therefore, if your script will require allocation of additionnal 1MB, 5MB, or 15MB of memory, its memory footprint will NOT grow beyond the high-point of 39MB. 好的一面是,在程序的整个生命周期内可以释放15MB的内存分配(不考虑内存碎片问题) - 因此,如果你的脚本需要分配额外的1MB,5MB或15MB内存,它的内存占用量不会超过39MB的高点。 And if it requires 17MB of additional memory, the resulting memory footprint will only be only 41MB, not 56MB. 如果需要17MB的额外内存,最终的内存占用量仅为41MB,而不是56MB。

  5. If this memory arithmetic is not satisfactory to you (eg if your original array was 500MB and you aren't willing to tolerate the program memory footprint rising to 1GB), then Dallaylaen's answer below is a great algorithm for doing the task without extra memory allocation 如果这个内存算术对你不满意(例如,如果你的原始数组是500MB并且你不愿意容忍程序内存占用上升到1GB),那么Dallaylaen的答案是一个很好的算法,可以在没有额外内存分配的情况下完成任务

Reverse the order of your loop and you can use splice : 颠倒循环的顺序,你可以使用splice

for(my $i = $#array; $i >= 0; --$i) {
    #...
}

If you do @$array = grep { ... } @$array anyway, why not just stick with grep { $_ !~ /hi/ } ? 如果你做@$array = grep { ... } @$array ,为什么不坚持使用grep { $_ !~ /hi/ }

However, if you are really memory-bound, you may try to go from the top: 但是,如果你真的受内存限制,你可以尝试从顶部开始:

my $i = @$array;
while ($i-->0) {
    splice @$array, $i, 1 if $array->[$i] =~ /hi/;
}; 

But this has a worst-case performance of n^2, so it may be even better to write in C-with-dollars instead of real Perl: 但这有一个n ^ 2的最坏情况,所以用C-with-dollars而不是真正的Perl编写可能更好:

my $array = [qw(ok now what is hi the matter)];
my $to = 0;
# move elements backwards
for (my $from=0; $from < @$array; $from++) {
     $array->[$from] =~ /hi/ and next;
     $array->[$to++] = $array->[$from];
};
# remove tail 
splice @$array, $to; 
print join ":", @$array;

Still I don't know why delete $array->[$i] won't work, it works on perl 5.10 and 5.8 I currently have at hand. 我仍然不知道为什么delete $array->[$i]将不起作用,它适用于perl 5.10和5.8我目前手边有。

Loop through each key, push each item to remove into an array, then use a final delete - one swoop! 循环遍历每个键,将每个项目推送到一个数组中,然后使用最终删除 - 一举!

 foreach my $key(keys %$my_array) {     
    my $val= $my_array->{$key};    

    if ($val eq "BAD") {
        push (@unwanted,$key);
    }            
}
delete @{$my_array}{@unwanted};
sub zonk {
  $array=shift; # this is a reference of an array
  foreach $i (0..$#$array) { # I saw some say to avoid last element to get size

    print $array->[$i],"\n";

    if ($array->[$i] =~ /hi/) {
      delete @{$array}[$i];
    }

  }
  @$array = grep{$_} @$array; # removes empty elements
}
zonk(\@a);
print join(':',@a);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM