배열에는 많은 데이터가 있으며 두 개의 요소를 삭제해야합니다.
아래는 내가 사용중인 코드 스 니펫입니다.
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 John Siracusa는 내 예제에 버그가 있다고 정확하게 지적했습니다. 수정되었습니다. 죄송합니다.
my ($index) = grep { $arr[$_] eq 'foo' } 0..$#arr; if (defined $index) {splice(@arr, $index, 1); }
-첫 경기
이것이 당신이 많이 할 일입니까? 그렇다면 다른 데이터 구조를 고려할 수 있습니다. Grep은 매번 전체 어레이를 검색 할 것이며 대형 어레이의 경우 비용이 많이들 수 있습니다. 속도가 문제라면 대신 해시를 사용하는 것이 좋습니다.
귀하의 예에서 키는 숫자이고 값은 해당 숫자의 요소 수입니다.
변경하면
my @del_indexes = grep { $arr[$_] eq 'foo' } 0..$#arr;
...에
my @del_indexes = reverse(grep { $arr[$_] eq 'foo' } 0..$#arr);
이렇게하면 배열 뒷면에서 먼저 요소를 제거하여 배열 번호 재 지정 문제를 방지 할 수 있습니다. splice ()를 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
오프셋 으로 전환하는 것이 얼마나 어려운지 설명 합니다. 따라서 더 완전한 대답으로 만듭니다.
목록 항목에 대한 테스트를 색인으로 전환하는 효율적인 (예 : 1 회 통과) 알고리즘을 갖기 위해 거쳐야 하는 회전 을 살펴보십시오 . 그리고 그것은 전혀 직관적이지 않습니다.
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 배열 요소의 존재를 삭제하거나 확인하는 개념은 개념적으로 일관성이 없으며 놀라운 동작을 초래할 수 있습니다." (문서의 이전 단락에는 모든 세부 사항이 있습니다).
내가 찾은 최고는 "undef"와 "grep"의 조합이었습니다.
foreach $index ( @list_of_indexes_to_be_skiped ) {
undef($array[$index]);
}
@array = grep { defined($_) } @array;
그게 트릭입니다! 페데리코
grep
끝에는 제거됩니다.
내가 grep 및 map 솔루션을 벤치마킹했는지 확인하기 위해 먼저 일치하는 요소 (제거 할 요소)의 색인을 검색 한 다음 색인을 검색하지 않고 grep으로 요소를 직접 제거합니다. 나는 Sam이 그의 질문을 할 때 제안한 첫 번째 해결책이 이미 가장 빠른 것 같습니다.
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에 의존해야한다고 생각합니다. ;-)