배열을 반복하지 않고 배열에 값이 있는지 확인하는 방법을 찾으려고합니다.
매개 변수에 대한 파일을 읽고 있습니다. 처리하고 싶지 않은 매개 변수의 긴 목록이 있습니다. 이러한 원치 않는 매개 변수를 배열에 배치했습니다 @badparams
.
새 매개 변수를 읽고 싶습니다.에없는 경우 @badparams
처리하십시오. 에이 (가) 있으면 @badparams
다음 읽기로 이동하십시오.
배열을 반복하지 않고 배열에 값이 있는지 확인하는 방법을 찾으려고합니다.
매개 변수에 대한 파일을 읽고 있습니다. 처리하고 싶지 않은 매개 변수의 긴 목록이 있습니다. 이러한 원치 않는 매개 변수를 배열에 배치했습니다 @badparams
.
새 매개 변수를 읽고 싶습니다.에없는 경우 @badparams
처리하십시오. 에이 (가) 있으면 @badparams
다음 읽기로 이동하십시오.
답변:
간단히 배열을 해시로 바꾸십시오.
my %params = map { $_ => 1 } @badparams;
if(exists($params{$someparam})) { ... }
목록에 더 많은 (고유 한) 매개 변수를 추가 할 수도 있습니다.
$params{$newparam} = 1;
나중에 (고유 한) 매개 변수 목록을 다시 가져옵니다.
@badparams = keys %params;
1
다시 설정 합니다.
가장 일반적인 용도-특히 짧은 배열 (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()
당신의 가치를 발견하자마자 멈출 것입니다.
자세한 내용은 블로그 게시물을 확인하십시오.
grep
리소스 를 살펴보면를 사용하지 마십시오 .
if ( grep( /^$value$/, @badparams ) ) {
print "found";
}
for (@badparams) {
if ($_ eq $value) {
print "found";
last;
}
}
my %hash = map {$_ => 1} @badparams;
print "found" if (exists $hash{$value});
(Perl 5.10에 추가 된 Mark는 Perl 5.18에서 실험적 임).
use experimental 'smartmatch'; # for perl 5.18
print "found" if ($value ~~ @badparams);
List::MoreUtils
use List::MoreUtils qw(any);
@badparams = (1,2,3);
$value = 1;
print "found" if any {$_ == $value} @badparams;
@eakssjo의 벤치 마크 가 깨졌습니다-루프에서 해시 생성과 루프에서 정규 표현식 생성 측정. 고정 버전 (및 추가 한 List::Util::first
및 List::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)
List::Util::first
일치하는 항목을 찾으면 반복이 중지되므로 사용 속도가 더 빠릅니다.
grep
있다 크게 전자가 O (N)와 후자 O (1)이기 때문에, 느린 해시를 생성하고 검색하는 것보다. 루프 외부에서 해시 생성을 한 번만 수행하고 정규식을 미리 계산하여 메소드 만 측정 하십시오 (내 답변 참조 ).
이를 수행 할 수있는 두 가지 방법이 있습니다. 다른 게시물에서 제안한대로 조회 값을 조회 테이블의 해시에 던져 넣을 수 있습니다. (또 다른 관용구를 추가하겠습니다.)
my %bad_param_lookup;
@bad_param_lookup{ @bad_params } = ( 1 ) x @bad_params;
그러나 주로 단어 문자이며 메타가 너무 많지 않은 경우 정규식 대체로 덤프 할 수 있습니다.
use English qw<$LIST_SEPARATOR>;
my $regex_str = do {
local $LIST_SEPARATOR = '|';
"(?:@bad_params)";
};
# $front_delim and $back_delim being any characters that come before and after.
my $regex = qr/$front_delim$regex_str$back_delim/;
이 솔루션은 원하는 "잘못된 값"유형에 맞게 조정해야합니다. 다시 말하지만, 특정 유형의 문자열에는 전혀 부적절 할 수 있으므로 주의하십시오 .
@bad_param_lookup{@bad_params} = ()
있지만 exists
멤버십을 테스트하는 데 사용해야 합니다.
my @badparams = (1,2,5,7,'a','zzz');
my $badparams = join('|',@badparams); # '|' or any other character not present in params
foreach my $par (4,5,6,7,'a','z','zzz')
{
if ($badparams =~ /\b$par\b/)
{
print "$par is present\n";
}
else
{
print "$par is not present\n";
}
}
숫자 선행 공백이 일치하는지 확인할 수 있습니다.