2010-12-11 3 views
5

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

@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 

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

удаления аргумента не является HASH элемента или срез на линии hi.pl 10.

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

Приложение: Это отлично работает (я имею в виду каждую прокомментированную строку) на моей машине linux (ubuntu 9.10, perl 5.10), но приведенная выше ошибка находится на моем ящике Windows 7 при работе с использованием perl 5.005_03. Модернизация не является вариантом.

Благодаря

ответ

0
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); 
+0

Не работает на моем ящике Windows 7 с Perl 5.005. Я добавил добавление к моему первоначальному вопросу. – Shawn

6

Почему не Grep с самого начала идти?

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

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

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

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

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

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

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

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

+0

это выглядит как ответ для меня. если нет дополнительного использования памяти ... комментарий ниже говорит, что это так – Shawn

+0

@Shawn - исправил ответ, надеюсь, исчерпывающие замечания: проблемы с памятью. – DVK

2

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

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

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

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

Я ужасно ошибаюсь: Perl будет использовать память в случае @array = grep {...} @array. Go @DVK! – Dallaylaen

+0

, если Perl повторно использует память grep, я буду использовать это точно! люблю мой unix grep ... – Shawn

+0

это бит TAD более сложный, чем это, но достаточно близкое резюме. Я отправлю комментарий о моем собственном ответе re: memory подробно. Тем не менее, ваше решение является моим фаворитом, поскольку ограничено памятью - +1! – DVK

4

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

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

классный .... но заставляет меня думать, что итераторы в Perl немного неудобны. – Shawn

+0

@Shawn - это потому, что это не «итератор» в смысле объекта, указывающего на связанный список. Я не думаю, что у Perl есть настоящий «итератор», а не просто создание класса LinkedList вручную. – DVK

+0

@Shawn - в основном, массивы Perl ведут себя очень близко к связанным спискам, за исключением случайных вставок/удалений в середине списка, не являющихся O (1), как вы заметили (но shift/unshift/pop/push - O (1)) – DVK

0

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

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

    if ($val eq "BAD") { 
     push (@unwanted,$key); 
    }    
} 
delete @{$my_array}{@unwanted}; 
Смежные вопросы