Как я могу удалить элемент ссылочного массива? - PullRequest
5 голосов
/ 11 декабря 2010

Я хочу удалить элементы из нескольких больших массивов с помощью подпрограммы. Я использую ссылку, чтобы избежать копирования в подпункте.

@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);

Если я запускаю программу выше, как я получаю:

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

Но если вместо этого я буду использовать любую из закомментированных строк, я получу:

аргумент delete не является элементом или фрагментом HASH в строке hi.pl 10.

Первоначально я пытался сращивать, но затем индексы сместились и запутали итерацию. Было бы неплохо узнать все методы, упомянутые в этом посте, однако наиболее эффективным является то, что я ищу:)

Приложение: Это прекрасно работает (я имею в виду каждую закомментированную строку) на моей машине с Linux (Ubuntu 9.10, Perl 5.10), но вышеупомянутая ошибка на моем компьютере с Windows 7 при работе с Perl 5.005_03 Обновление не вариант.

Спасибо

Ответы [ 5 ]

6 голосов
/ 11 декабря 2010

Почему бы не grep с самого начала?

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

Небольшой набор заметок для уточнения вопросов, возникших в комментариях:

  1. Этот метод (или любой метод, использующий grep, включая исходный код автора) увеличит использование памяти сценарием на размер нового результирующего массива.

    например. если скрипт (без первого массива) занимал 10 МБ памяти, исходный массив занимал 15 МБ памяти, а результирующий массив занимал 14 МБ памяти, то общий объем памяти вашей программы увеличится с 25 МБ до 39 МБ, когда grep работает.

  2. После завершения grep память, используемая исходным массивом, станет доступной для сбора мусора (с некоторыми оговорками, не относящимися к этому сообщению).

  3. Однако - и это важно - даже если исходные 15 МБ данных собираются сборщиком мусора, , что 15 МБ не будет возвращено Perl операционной системе - например, объем памяти скрипта останется 39 МБ и не уменьшится до 24 МБ даже после сборки мусора.

  4. С хорошей стороны, эти освобожденные 15 МБ будут доступны для выделения памяти в течение оставшейся части жизни вашей программы (оставляя в стороне проблемы фрагментации памяти) - следовательно, если ваш сценарий потребует выделения дополнительных 1 МБ, 5 МБ или 15 МБ памяти, ее объем памяти НЕ будет превышать 39 МБ. И если для этого требуется 17 МБ дополнительной памяти, полученная в результате память будет занимать всего 41 МБ, а не 56 МБ.

  5. Если эта арифметика памяти вам не подходит (например, если ваш исходный массив составлял 500 МБ, и вы не хотите мириться с увеличением объема памяти программы до 1 ГБ), тогда ответ Даллайлаена ниже отличный алгоритм для выполнения задачи без дополнительного выделения памяти

4 голосов
/ 11 декабря 2010

Обратный порядок вашего цикла, и вы можете использовать splice:

for(my $i = $#array; $i >= 0; --$i) {
    #...
}
2 голосов
/ 11 декабря 2010

Если вы все равно наберете @$array = grep { ... } @$array, почему бы просто не придерживаться grep { $_ !~ /hi/ }?

Однако, если вы действительно ограничены в памяти, вы можете попробовать перейти сверху:

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

Но в худшем случае это производительность n ^ 2, поэтому может быть даже лучше написать C-with-долларов вместо реального 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;

Тем не менее, я не знаю, почему delete $array->[$i] не будет работать, он работает на Perl 5.10 и 5.8, которые у меня сейчас под рукой.

0 голосов
/ 19 мая 2016

Прокручивайте каждую клавишу, нажимайте каждый элемент для удаления в массив, затем используйте окончательное удаление - одним махом!

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

    if ($val eq "BAD") {
        push (@unwanted,$key);
    }            
}
delete @{$my_array}{@unwanted};
0 голосов
/ 11 декабря 2010
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);
...