Development Tip

Perl의 배열에서 값을 삭제하는 가장 좋은 방법은 무엇입니까?

yourdevel 2020. 10. 14. 21:19
반응형

Perl의 배열에서 값을 삭제하는 가장 좋은 방법은 무엇입니까?


배열에는 많은 데이터가 있으며 두 개의 요소를 삭제해야합니다.

아래는 내가 사용중인 코드 스 니펫입니다.

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는 내 예제에 버그가 있다고 정확하게 지적했습니다. 수정되었습니다. 죄송합니다.


스플 라이스 는 인덱스로 배열 요소를 제거합니다. 예제에서와 같이 grep을 사용하여 검색하고 제거하십시오.


이것은 당신이 많이 할 일입니까? 그렇다면 다른 데이터 구조를 고려할 수 있습니다. 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);
}

귀하의 솔루션이 가장 간단하고 유지 관리가 용이하다고 생각합니다.

나머지 포스트에서는 요소에 대한 테스트를 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;
}

스 플라이 싱 대신 어레이 슬라이싱을 사용할 수 있습니다. 유지하려는 인덱스를 반환하고 슬라이싱을 사용하려면 Grep :

my @arr = ...;
my @indicesToKeep = grep { $arr[$_] ne 'foo' } 0..$#arr;
@arr = @arr[@indiciesToKeep];

나는 사용한다:

delete $array[$index];

Perldoc 삭제 .


배열 인 경우 'something'항목을 모두 삭제합니다.

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.


비 캡처 그룹 및 제거 할 항목의 파이프 delim 목록을 사용할 수 있습니다.


perl -le '@ar=(1 .. 20);@x=(8,10,3,17);$x=join("|",@x);@ar=grep{!/^(?:$x)$/o} @ar;print "@ar"'

내가 찾은 최고는 "undef"와 "grep"의 조합이었습니다.

foreach $index ( @list_of_indexes_to_be_skiped ) {
      undef($array[$index]);
}
@array = grep { defined($_) } @array;

그게 트릭입니다! 페데리코


Just to be sure I have benchmarked grep and map solutions, first searching for indexes of matched elements (those to remove) and then directly removing the elements by grep without searching for the indexes. I appears that the first solution proposed by Sam when asking his question was already the fastest.

    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;
      },
    });

The result is:

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

As shown by elapsed times, it's useless to try to implement a remove function using either grep or map defined indexes. Just grep-remove directly.

Before testing I was thinking "map1" would be the most efficient... I should more often rely on Benchmark I guess. ;-)


If you know the array index, you can delete() it. The difference between splice() and delete() is that delete() does not renumber the remaining elements of the array.


A similar code I once wrote to remove strings not starting with SB.1 from an array of strings

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);}
}

You can simply do this:

my $input_Color = 'Green';
my @array = qw(Red Blue Green Yellow Black);
@array = grep {!/$input_Color/} @array;
print "@array";

참고URL : https://stackoverflow.com/questions/174292/what-is-the-best-way-to-delete-a-value-from-an-array-in-perl

반응형