Das Array hat viele Daten und ich muss zwei Elemente löschen.
Unten ist das Code-Snippet, das ich verwende.
my @array = (1,2,3,4,5,5,6,5,4,9);
my $element_omitted = 5;
@array = grep { $_ != $element_omitted } @array;
Das Array hat viele Daten und ich muss zwei Elemente löschen.
Unten ist das Code-Snippet, das ich verwende.
my @array = (1,2,3,4,5,5,6,5,4,9);
my $element_omitted = 5;
@array = grep { $_ != $element_omitted } @array;
Antworten:
Verwenden Sie Spleiß, wenn Sie den Index des Elements, das Sie löschen möchten, bereits kennen.
Grep funktioniert, wenn Sie suchen.
Wenn Sie viele davon ausführen müssen, erhalten Sie eine viel bessere Leistung, wenn Sie Ihr Array in sortierter Reihenfolge halten, da Sie dann eine binäre Suche durchführen können, um den erforderlichen Index zu finden.
Wenn es in Ihrem Kontext sinnvoll ist, sollten Sie einen "magischen Wert" für gelöschte Datensätze verwenden, anstatt sie zu löschen, um Datenverschiebungen zu vermeiden. Setzen Sie beispielsweise gelöschte Elemente auf undef. Dies hat natürlich seine eigenen Probleme (wenn Sie die Anzahl der "Live" -Elemente kennen müssen, müssen Sie diese separat verfolgen usw.), kann sich jedoch je nach Anwendung lohnen.
Bearbeiten Eigentlich jetzt, wo ich einen zweiten Blick darauf werfen werde - benutze nicht den obigen Grep-Code. Es wäre effizienter, den Index des Elements zu finden, das Sie löschen möchten, und ihn dann mit Spleiß zu löschen (der Code, den Sie haben, sammelt alle nicht übereinstimmenden Ergebnisse.)
my $index = 0;
$index++ until $arr[$index] eq 'foo';
splice(@arr, $index, 1);
Dadurch wird das erste Vorkommen gelöscht. Das Löschen aller Vorkommen ist sehr ähnlich, außer dass Sie alle Indizes in einem Durchgang erhalten möchten:
my @del_indexes = grep { $arr[$_] eq 'foo' } 0..$#arr;
Der Rest bleibt als Übung für den Leser - denken Sie daran, dass sich das Array beim Spleißen ändert!
Edit2 John Siracusa hat richtig darauf hingewiesen, dass ich einen Fehler in meinem Beispiel hatte. Behoben , tut mir leid.
my ($index) = grep { $arr[$_] eq 'foo' } 0..$#arr; if (defined $index) {splice(@arr, $index, 1); }
- für das erste Spiel
Wirst du das viel tun? In diesem Fall möchten Sie möglicherweise eine andere Datenstruktur in Betracht ziehen. Grep wird jedes Mal das gesamte Array durchsuchen und nach einem großen Array suchen, was ziemlich kostspielig sein kann. Wenn Geschwindigkeit ein Problem ist, sollten Sie stattdessen einen Hash verwenden.
In Ihrem Beispiel wäre der Schlüssel die Zahl und der Wert die Anzahl der Elemente dieser Zahl.
wenn du dich änderst
my @del_indexes = grep { $arr[$_] eq 'foo' } 0..$#arr;
zu
my @del_indexes = reverse(grep { $arr[$_] eq 'foo' } 0..$#arr);
Dies vermeidet das Problem der Umnummerierung des Arrays, indem zuerst Elemente von der Rückseite des Arrays entfernt werden. Wenn Sie einen Spleiß () in eine foreach-Schleife einfügen, wird @arr bereinigt. Relativ einfach und lesbar ...
foreach $item (@del_indexes) {
splice (@arr,$item,1);
}
Sie können Array-Slicing anstelle von Spleißen verwenden. Grep, um die gewünschten Indizes zurückzugeben und Slicing zu verwenden:
my @arr = ...;
my @indicesToKeep = grep { $arr[$_] ne 'foo' } 0..$#arr;
@arr = @arr[@indiciesToKeep];
@arr = @arr[grep ...]
was mir besonders gefällt. Ich bin mir nicht sicher, wie effizient es ist, aber ich werde es verwenden, weil es nicht schlechter sein kann als die anderen Lösungen.
Der Rest des Beitrags dokumentiert die Schwierigkeit, Tests an Elementen in splice
Offsets umzuwandeln. Dies macht es zu einer vollständigeren Antwort.
Schauen Sie sich die Drehungen an, die Sie durchlaufen müssen, um einen effizienten Algorithmus (dh einen Durchgang) zu erhalten, mit dem Tests für Listenelemente in Indizes umgewandelt werden können. Und es ist überhaupt nicht so intuitiv.
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
. Außerdem aus dem durch ringø verknüpften Dokument: "WARNUNG: Das Aufrufen von delete für Array-Werte wird dringend empfohlen. Das Löschen oder Überprüfen der Existenz von Perl-Array-Elementen ist konzeptionell nicht kohärent und kann zu überraschendem Verhalten führen." (Der vorherige Absatz im Dokument enthält alle wichtigen Details).
Löschen Sie alle Vorkommen von 'etwas' wenn Array.
Basierend auf den Antworten von 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);
Jedes Mal, wenn wir den Index entfernen @arr
, wird der nächste korrekte Index gelöscht $_-current_loop_step
.
Das Beste, was ich gefunden habe, war eine Kombination aus "undef" und "grep":
foreach $index ( @list_of_indexes_to_be_skiped ) {
undef($array[$index]);
}
@array = grep { defined($_) } @array;
Das macht den Trick! Federico
grep
am Ende sie dann entfernt.
Nur um sicherzugehen, dass ich Grep- und Map-Lösungen verglichen habe, indem ich zuerst nach Indizes übereinstimmender Elemente (die zu entfernender) gesucht und dann die Elemente direkt durch Grep entfernt habe, ohne nach den Indizes zu suchen. Ich scheine, dass die erste von Sam vorgeschlagene Lösung, als er seine Frage stellte, bereits die schnellste war.
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;
},
});
Das Ergebnis ist:
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
Wie die verstrichenen Zeiten zeigen, ist es sinnlos zu versuchen, eine Entfernungsfunktion entweder mit grep- oder map-definierten Indizes zu implementieren. Einfach direkt grep-entfernen.
Vor dem Testen dachte ich, "map1" wäre am effizientesten ... Ich sollte mich öfter auf Benchmark verlassen, denke ich. ;-);
Wenn Sie den Array-Index kennen, können Sie ihn löschen () . Der Unterschied zwischen splice () und delete () besteht darin, dass delete () die verbleibenden Elemente des Arrays nicht neu nummeriert.
Ein ähnlicher Code, den ich einmal geschrieben habe, um Zeichenfolgen, die nicht mit SB.1 beginnen, aus einem Array von Zeichenfolgen zu entfernen
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);}
}