配列には大量のデータがあり、2つの要素を削除する必要があります。
以下は私が使用しているコードスニペットです。
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);
それは最初の出現を削除します。すべてのオカレンスを削除することは非常によく似ていますが、1回のパスですべてのインデックスを取得する必要があります。
my @del_indexes = grep { $arr[$_] eq 'foo' } 0..$#arr;
残りは読者のための練習問題として残されています。スプライスすると配列が変化することに注意してください。
Edit2 John Siracusaは、私の例にバグがあったことを正しく指摘しました。
splice は、インデックスによって配列要素を削除します。例のようにgrepを使用して、検索および削除します。
これはあなたがたくさんやろうとしていることですか?その場合、別のデータ構造を検討することをお勧めします。 Grepは毎回配列全体を検索しますが、大きな配列は非常にコストがかかります。速度が問題になる場合は、代わりにハッシュの使用を検討してください。
あなたの例では、キーは数値であり、値はその数値の要素の数になります。
変えたら
my @del_indexes = grep { $arr[$_] eq 'foo' } 0..$#arr;
に
my @del_indexes = reverse(grep { $arr[$_] eq 'foo' } 0..$#arr);
これにより、最初に配列の後ろから要素を削除することで、配列の番号の付け直しの問題を回避できます。 foreachループにsplice()を入れると、@ arrがクリーンアップされます。比較的シンプルで読みやすい...
foreach $item (@del_indexes) {
splice (@arr,$item,1);
}
投稿の残りの部分では、要素のテストをsplice
オフセットに変えることの難しさを文書化しています。したがって、それをよりcomplete答えにします。
gyrationsを見てくださいインデックスへのアイテム。そして、それはまったく直感的ではありません。
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;
}
スプライシングの代わりに配列スライシングを使用できます。 Grepは、保持したいインデックスを返し、スライスを使用します。
my @arr = ...;
my @indicesToKeep = grep { $arr[$_] ne 'foo' } 0..$#arr;
@arr = @arr[@indiciesToKeep];
私が見つけた最高のものは、「undef」と「grep」の組み合わせでした。
foreach $index ( @list_of_indexes_to_be_skiped ) {
undef($array[$index]);
}
@array = grep { defined($_) } @array;
それはトリックです!フェデリコ
配列の場合、「何か」の出現をすべて削除します。
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
になります。
非キャプチャグループと削除するアイテムのパイプ区切りリストを使用できます。
Perl -le '@ar=(1 .. 20);@x=(8,10,3,17);$x=join("|",@x);@ar=grep{!/^(?:$x)$/o} @ar;print "@ar"'
単にgrepとマップソリューションのベンチマークを行っていることを確認するために、最初に一致する要素(削除する要素)のインデックスを検索し、次にインデックスを検索せずに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」が最も効率的だと考えていました...ベンチマークにもっと頻繁に頼るべきです。 ;-)
文字列の配列から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);}
}
これを簡単に行うことができます:
my $input_Color = 'Green';
my @array = qw(Red Blue Green Yellow Black);
@array = grep {!/$input_Color/} @array;
print "@array";
配列インデックスがわかっている場合は、 delete() itを使用できます。 splice()とdelete()の違いは、delete()が配列の残りの要素の番号を付け直さないことです。