[perl] Perl 배열에 특정 값이 포함되어 있는지 어떻게 확인할 수 있습니까?

배열을 반복하지 않고 배열에 값이 있는지 확인하는 방법을 찾으려고합니다.

매개 변수에 대한 파일을 읽고 있습니다. 처리하고 싶지 않은 매개 변수의 긴 목록이 있습니다. 이러한 원치 않는 매개 변수를 배열에 배치했습니다 @badparams.

새 매개 변수를 읽고 싶습니다.에없는 경우 @badparams처리하십시오. 에이 (가) 있으면 @badparams다음 읽기로 이동하십시오.



답변

간단히 배열을 해시로 바꾸십시오.

my %params = map { $_ => 1 } @badparams;

if(exists($params{$someparam})) { ... }

목록에 더 많은 (고유 한) 매개 변수를 추가 할 수도 있습니다.

$params{$newparam} = 1;

나중에 (고유 한) 매개 변수 목록을 다시 가져옵니다.

@badparams = keys %params;


답변

가장 일반적인 용도-특히 짧은 배열 (1000 개 이하) 및 요구에 가장 적합한 최적화가 무엇인지 잘 모르는 코더.

# $value can be any regex. be safe
if ( grep( /^$value$/, @array ) ) {
  print "found it";
}

grep은 배열의 첫 번째 값이 일치하더라도 모든 값을 통과한다고 언급했습니다. 이것은 사실이지만 grep은 여전히 ​​대부분의 경우 매우 빠릅니다 . 짧은 배열 (1000 개 미만의 항목)에 대해 이야기하는 경우 대부분의 알고리즘은 어쨌든 매우 빠릅니다. 매우 긴 배열 (1,000,000 품목)에 대해 이야기하고 있다면 grep은 항목이 배열의 첫 번째인지 중간인지 마지막인지에 관계없이 매우 빠릅니다.

더 긴 어레이를위한 최적화 사례 :

배열이 정렬 된 경우 “이진 검색”을 사용하십시오.

은 IF 와 동일한 배열을 반복적으로 검색하여 여러 번, 제 해시에 복사 한 다음 해시를 확인한다. 메모리가 중요한 경우 각 항목을 배열에서 해시로 이동하십시오. 더 메모리 효율적이지만 원래 배열을 파괴합니다.

경우 같은 값이 반복적으로 검색되는 배열 내에서 유유히 캐시를 구축 할 수 있습니다. (각 항목을 검색 할 때 먼저 검색 결과가 지속 된 해시에 저장되어 있는지 확인하십시오. 검색 결과가 해시에서 발견되지 않으면 배열을 검색하고 결과를 지속 된 해시에 넣어 다음에 우리가 해시에서 찾아서 검색을 건너 뜁니다.

참고 : 이러한 최적화는 긴 배열을 처리 할 때만 더 빠릅니다. 과도하게 최적화하지 마십시오.


답변

다음과 같이 Perl 5.10 에서 스마트 매치 기능을 사용할 수 있습니다 .

리터럴 값 조회의 경우 아래 작업을 수행하면 트릭이 수행됩니다.

if ( "value" ~~ @array ) 

스칼라 조회의 경우 아래 작업을 수행하면 위와 같이 작동합니다.

if ($val ~~ @array)

아래 작업을 수행하는 인라인 배열의 경우 위와 같이 작동합니다.

if ( $var ~~ ['bar', 'value', 'foo'] ) 

에서 펄 5.18 그러므로 당신이 설정하여 경고를 해제해야 실험적으로 플래그가 smartmatch 실험 스크립트 / 모듈에 아래 추가하여 프라그 :

use experimental 'smartmatch';

또는 smartmatch의 사용을 피하려면 Aaron이 말한 것처럼 :

if ( grep( /^$value$/, @array ) ) {
  #TODO:
}


답변

이 블로그 게시물 에서는이 질문에 대한 최상의 답변에 대해 설명합니다.

간단한 요약으로 CPAN 모듈을 설치할 수있는 경우 가장 읽기 쉬운 솔루션은 다음과 같습니다.

any(@ingredients) eq 'flour';

또는

@ingredients->contains('flour');

그러나 더 일반적인 관용구는 다음과 같습니다.

any { $_ eq 'flour' } @ingredients

그러나 first()기능을 사용하지 마십시오 ! 코드의 의도를 전혀 나타내지 않습니다. ~~“스마트 일치”연산자를 사용하지 마십시오. 작동하지 않습니다 . 그리고 grep()해시와 함께 솔루션을 사용 하거나 사용하지 마십시오 . 전체 목록을 반복합니다.

any() 당신의 가치를 발견하자마자 멈출 것입니다.

자세한 내용은 블로그 게시물을 확인하십시오.


답변

방법 1 : grep (값이 정규식 일 것으로 예상되는 동안주의 할 수 있음)

grep리소스 를 살펴보면를 사용하지 마십시오 .

if ( grep( /^$value$/, @badparams ) ) {
  print "found";
}

방법 2 : 선형 검색

for (@badparams) {
    if ($_ eq $value) {
       print "found";
       last;
    }
}

방법 3 : 해시 사용

my %hash = map {$_ => 1} @badparams;
print "found" if (exists $hash{$value});

방법 4 : 스마트 매치

(Perl 5.10에 추가 된 Mark는 Perl 5.18에서 실험적 임).

use experimental 'smartmatch';  # for perl 5.18
print "found" if ($value ~~ @badparams);

방법 5 : 모듈 사용 List::MoreUtils

use List::MoreUtils qw(any);
@badparams = (1,2,3);
$value = 1;
print "found" if any {$_ == $value} @badparams;


답변

@eakssjo의 벤치 마크 가 깨졌습니다-루프에서 해시 생성과 루프에서 정규 표현식 생성 측정. 고정 버전 (및 추가 한 List::Util::firstList::MoreUtils::any) :

use List::Util qw(first);
use List::MoreUtils qw(any);
use Benchmark;

my @list = ( 1..10_000 );
my $hit = 5_000;
my $hit_regex = qr/^$hit$/; # precompute regex
my %params;
$params{$_} = 1 for @list;  # precompute hash
timethese(
    100_000, {
        'any' => sub {
            die unless ( any { $hit_regex } @list );
        },
        'first' => sub {
            die unless ( first { $hit_regex } @list );
        },
        'grep' => sub {
            die unless ( grep { $hit_regex } @list );
        },
        'hash' => sub {
            die unless ( $params{$hit} );
        },
    });

그리고 결과 (@eakssjo의 답변보다 10 배 더 많은 100_000 회 반복)입니다.

Benchmark: timing 100000 iterations of any, first, grep, hash...
       any:  0 wallclock secs ( 0.67 usr +  0.00 sys =  0.67 CPU) @ 149253.73/s (n=100000)
     first:  1 wallclock secs ( 0.63 usr +  0.01 sys =  0.64 CPU) @ 156250.00/s (n=100000)
      grep: 42 wallclock secs (41.95 usr +  0.08 sys = 42.03 CPU) @ 2379.25/s (n=100000)
      hash:  0 wallclock secs ( 0.01 usr +  0.00 sys =  0.01 CPU) @ 10000000.00/s (n=100000)
            (warning: too few iterations for a reliable count)


답변

사용하기 편리하지만 해시 변환 솔루션은 상당히 많은 비용이 들기 때문에 문제가되었습니다.

#!/usr/bin/perl
use Benchmark;
my @list;
for (1..10_000) {
    push @list, $_;
}

timethese(10000, {
  'grep'    => sub {
            if ( grep(/^5000$/o, @list) ) {
                # code
            }
        },
  'hash'    => sub {
            my %params = map { $_ => 1 } @list;
            if ( exists($params{5000}) ) {
                # code
            }
        },
});

벤치 마크 테스트 결과 :

Benchmark: timing 10000 iterations of grep, hash...
          grep:  8 wallclock secs ( 7.95 usr +  0.00 sys =  7.95 CPU) @ 1257.86/s (n=10000)
          hash: 50 wallclock secs (49.68 usr +  0.01 sys = 49.69 CPU) @ 201.25/s (n=10000)