У масиві багато даних, і мені потрібно видалити два елементи.
Нижче наведено фрагмент коду, який я використовую,
my @array = (1,2,3,4,5,5,6,5,4,9);
my $element_omitted = 5;
@array = grep { $_ != $element_omitted } @array;
У масиві багато даних, і мені потрібно видалити два елементи.
Нижче наведено фрагмент коду, який я використовую,
my @array = (1,2,3,4,5,5,6,5,4,9);
my $element_omitted = 5;
@array = grep { $_ != $element_omitted } @array;
Відповіді:
Використовуйте сплайсинг, якщо ви вже знаєте індекс елемента, який потрібно видалити.
Grep працює, якщо ви шукаєте.
Якщо вам потрібно зробити багато з них, ви отримаєте набагато кращу продуктивність, якщо збережете масив у відсортованому порядку, оскільки тоді ви можете виконати двійковий пошук, щоб знайти необхідний індекс.
Якщо це має сенс у вашому контексті, ви можете розглянути можливість використання «магічного значення» для видалених записів, а не їх видалення, щоб заощадити на переміщенні даних - наприклад, встановіть для видалених елементів значення undef. Звичайно, у цього є свої проблеми (якщо вам потрібно знати кількість "живих" елементів, вам потрібно відстежувати це окремо тощо), але це може коштувати клопоту залежно від вашої програми.
Редагувати Власне тепер, коли я ще раз переглядаю - не використовуйте grep-код вище. Було б ефективніше знайти індекс елемента, який ви хочете видалити, а потім скористатися сплайсом, щоб видалити його (у вашому коді накопичуються всі невідповідні результати ..)
my $index = 0;
$index++ until $arr[$index] eq 'foo';
splice(@arr, $index, 1);
Це видалить перше входження. Видалення всіх випадків дуже схоже, за винятком того, що ви захочете отримати всі індекси за один прохід:
my @del_indexes = grep { $arr[$_] eq 'foo' } 0..$#arr;
Решта залишається вправою для читача - пам’ятайте, що масив змінюється під час з’єднання!
Edit2 Джон Сіракуза правильно зазначив, що у моєму прикладі була помилка .. виправлено, вибачте за це.
my ($index) = grep { $arr[$_] eq 'foo' } 0..$#arr; if (defined $index) {splice(@arr, $index, 1); }
- для першого матчу
Це те, чим ти збираєшся робити багато? Якщо так, ви можете розглянути іншу структуру даних. Grep збирається шукати весь масив кожного разу, і для великого масиву це може бути досить дорогим. Якщо швидкість є проблемою, то, можливо, ви захочете використовувати Hash.
У вашому прикладі ключем буде число, а значенням буде кількість елементів цього числа.
якщо ти переодягнешся
my @del_indexes = grep { $arr[$_] eq 'foo' } 0..$#arr;
до
my @del_indexes = reverse(grep { $arr[$_] eq 'foo' } 0..$#arr);
Це дозволяє уникнути проблеми перенумерації масиву, попередньо видаливши елементи із зворотного боку масиву. Поміщення сплайсингу () у цикл foreach очищає @arr. Порівняно простий і читабельний ...
foreach $item (@del_indexes) {
splice (@arr,$item,1);
}
Ви можете використовувати нарізування масиву замість зрощування. Grep для повернення індексів, які ви хочете зберегти, та використання нарізки:
my @arr = ...;
my @indicesToKeep = grep { $arr[$_] ne 'foo' } 0..$#arr;
@arr = @arr[@indiciesToKeep];
@arr = @arr[grep ...]
що мені особливо подобається. Я не впевнений, наскільки це ефективно, але я почну його використовувати, оскільки він не може бути гіршим за інші рішення.
Решта поштових документів ускладнює перетворення тестів на елементи на splice
зміщення. Таким чином, роблячи це більше повною відповіддю.
Подивіться на обертання, які вам потрібно пройти, щоб мати ефективний (тобто однопрохідний) алгоритм перетворення тестів на елементи списку в індекси. І це зовсім не так інтуїтивно.
sub array_remove ( \@& ) {
my ( $arr_ref, $test_block ) = @_;
my $sp_start = 0;
my $sp_len = 0;
for ( my $inx = 0; $inx <= $#$arr_ref; $inx++ ) {
local $_ = $arr_ref->[$inx];
next unless $test_block->( $_ );
if ( $sp_len > 0 && $inx > $sp_start + $sp_len ) {
splice( @$arr_ref, $sp_start, $sp_len );
$inx = $inx - $sp_len;
$sp_len = 0;
}
$sp_start = $inx if ++$sp_len == 1;
}
splice( @$arr_ref, $sp_start, $sp_len ) if $sp_len > 0;
return;
}
undef
. Крім того, з документа, зв’язаного ringø: "ПОПЕРЕДЖЕННЯ: Виклик видалення значень масиву настійно не рекомендується. Поняття видалення або перевірки існування елементів масиву Perl не є концептуально цілісним і може призвести до дивовижної поведінки." (попередній абзац у документі містить усі криваві деталі).
Видалити всі випадки "щось", якщо масив.
На основі відповідей SquareCog:
my @arr = ('1','2','3','4','3','2', '3','4','3');
my @dix = grep { $arr[$_] eq '4' } 0..$#arr;
my $o = 0;
for (@dix) {
splice(@arr, $_-$o, 1);
$o++;
}
print join("\n", @arr);
Кожного разу, коли ми видаляємо індекс з @arr
, наступним правильним індексом для видалення буде $_-current_loop_step
.
Найкраще, що я знайшов, - це поєднання "undef" та "grep":
foreach $index ( @list_of_indexes_to_be_skiped ) {
undef($array[$index]);
}
@array = grep { defined($_) } @array;
У цьому трюк! Федеріко
grep
в кінці потім видаляє їх.
Щоб бути впевненим, що я здійснив тестування grep та map рішень, спочатку шукаючи індекси відповідних елементів (ті, що видаляються), а потім безпосередньо видаляючи елементи grep без пошуку індексів. Мені здається, що перше рішення, яке запропонував Сем, ставлячи своє запитання, було вже найшвидшим.
use Benchmark;
my @A=qw(A B C A D E A F G H A I J K L A M N);
my @M1; my @G; my @M2;
my @Ashrunk;
timethese( 1000000, {
'map1' => sub {
my $i=0;
@M1 = map { $i++; $_ eq 'A' ? $i-1 : ();} @A;
},
'map2' => sub {
my $i=0;
@M2 = map { $A[$_] eq 'A' ? $_ : () ;} 0..$#A;
},
'grep' => sub {
@G = grep { $A[$_] eq 'A' } 0..$#A;
},
'grem' => sub {
@Ashrunk = grep { $_ ne 'A' } @A;
},
});
Результат:
Benchmark: timing 1000000 iterations of grem, grep, map1, map2...
grem: 4 wallclock secs ( 3.37 usr + 0.00 sys = 3.37 CPU) @ 296823.98/s (n=1000000)
grep: 3 wallclock secs ( 2.95 usr + 0.00 sys = 2.95 CPU) @ 339213.03/s (n=1000000)
map1: 4 wallclock secs ( 4.01 usr + 0.00 sys = 4.01 CPU) @ 249438.76/s (n=1000000)
map2: 2 wallclock secs ( 3.67 usr + 0.00 sys = 3.67 CPU) @ 272702.48/s (n=1000000)
M1 = 0 3 6 10 15
M2 = 0 3 6 10 15
G = 0 3 6 10 15
Ashrunk = B C D E F G H I J K L M N
Як показують минулі часи, марно намагатися реалізувати функцію видалення за допомогою grep або індексів, визначених картою. Просто grep-remove безпосередньо.
До тестування я думав, що "map1" буде найефективнішим ... Мені слід частіше покладатися на Benchmark. ;-)
Якщо вам відомий індекс масиву, його можна видалити () . Різниця між сплайсингом () та delete () полягає в тому, що delete () не перенумерує інші елементи масиву.
Подібний код, який я колись писав для видалення рядків, що не починаються з SB.1, з масиву рядків
my @adoSymbols=('SB.1000','RT.10000','PC.10000');
##Remove items from an array from backward
for(my $i=$#adoSymbols;$i>=0;$i--) {
unless ($adoSymbols[$i] =~ m/^SB\.1/) {splice(@adoSymbols,$i,1);}
}