Como posso verificar se uma matriz Perl contém um valor específico?

Eu estou tentando descobrir uma maneira de verificar a existência de um valor em uma matriz sem iterar através da matriz.

Eu estou lendo um arquivo para um parâmetro. Eu tenho uma longa lista de parâmetros com os quais não quero lidar. @badparams esses parâmetros indesejados em uma matriz @badparams .

Eu quero ler um novo parâmetro e se ele não existir em @badparams , processe-o. Se existir em @badparams , vá para a próxima leitura.

Basta transformar o array em um hash:

 my %params = map { $_ => 1 } @badparams; if(exists($params{$someparam})) { ... } 

Você também pode adicionar mais parâmetros (exclusivos) à lista:

 $params{$newparam} = 1; 

E mais tarde, obtenha uma lista de parâmetros (únicos) de volta:

 @badparams = keys %params; 

Melhor propósito geral – Especialmente matrizes curtas (1000 itens ou menos) e codificadores que não têm certeza de quais otimizações atendem melhor às suas necessidades.

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

Foi mencionado que o grep passa por todos os valores, mesmo que o primeiro valor da matriz corresponda. Isso é verdade, no entanto grep ainda é extremamente rápido para a maioria dos casos . Se você está falando de matrizes curtas (menos de 1000 itens), a maioria dos algoritmos será bem rápida de qualquer maneira. Se você estiver falando de matrizes muito longas (1.000.000 itens), o grep é aceitavelmente rápido, independentemente de o item ser o primeiro ou o meio ou o último no array.

Casos de otimização para matrizes mais longas:

Se sua matriz estiver classificada , use uma “pesquisa binária”.

Se a mesma matriz for repetidamente pesquisada várias vezes, copie-a em um hash primeiro e depois verifique o hash. Se a memory é uma preocupação, mova cada item da matriz para o hash. Mais memory eficiente, mas destrói o array original.

Se os mesmos valores forem pesquisados ​​repetidamente no array, construa um cache preguiçosamente. (à medida que cada item é pesquisado, primeiro verifique se o resultado da pesquisa foi armazenado em um hash persistente. Se o resultado da pesquisa não for encontrado no hash, pesquise o array e coloque o resultado no hash persistido para que da próxima vez encontre no hash e pule a pesquisa).

Nota: essas otimizações só serão mais rápidas ao lidar com matrizes longas. Não otimize demais.

Você pode usar o recurso smartmatch no Perl 5.10 da seguinte maneira:

Para uma consulta de valor literal, fazer o seguinte.

 if ( "value" ~~ @array ) 

Para pesquisa escalar, fazer abaixo funcionará como acima.

 if ($val ~~ @array) 

Para o array inline abaixo, funcionará como acima.

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

No Perl 5.18 o smartmatch é sinalizado como experimental, portanto, você precisa desativar os avisos ativando o pragma experimental , adicionando abaixo ao seu script / módulo:

 use experimental 'smartmatch'; 

Alternativamente, se você quiser evitar o uso de smartmatch – então como Aaron disse use:

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

Esta postagem do blog discute as melhores respostas para essa pergunta.

Como um breve resumo, se você pode instalar módulos CPAN, as soluções mais legíveis são:

 any(@ingredients) eq 'flour'; 

ou

 @ingredients->contains('flour'); 

No entanto, um idioma mais comum é:

 any { $_ eq 'flour' } @ingredients 

Mas por favor não use a first() function first() ! Ele não expressa a intenção do seu código. Não use o operador ~~ “Smart match”: ele está quebrado. E não use grep() nem a solução com um hash: eles percorrem toda a lista.

any() irá parar assim que encontrar o seu valor.

Confira a postagem do blog para mais detalhes.

Mesmo que seja conveniente de usar, parece que a solução convertida para hash custa bastante desempenho, o que foi um problema para mim.

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

Saída do teste de benchmark:

 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) 

O benchmark do @ eakssjo está quebrado – mede a criação de hashes no loop versus a criação de regexes em loop. Versão fixa (mais eu adicionei List::Util::first e 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} ); }, }); 

E resultado (é para 100_000 iterações, dez vezes mais que na resposta de @ eakssjo):

 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) 

Método 1: grep (pode ser cauteloso enquanto o valor é esperado para ser um regex).

Tente evitar usar o grep , se estiver procurando por resources.

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

Método 2: pesquisa linear

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

Método 3: usar um hash

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

Método 4: smartmatch

(adicionado em Perl 5.10, marcado é experimental em Perl 5.18).

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

Método 5: Usar o módulo principal List::MoreUtils

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

Você certamente quer um hash aqui. Coloque os parâmetros ruins como chaves no hash e decida se um determinado parâmetro existe no hash.

 our %bad_params = map { $_ => 1 } qw(badparam1 badparam2 badparam3) if ($bad_params{$new_param}) { print "That is a bad parameter\n"; } 

Se você está realmente interessado em fazer isso com um array, veja List::Util ou List::MoreUtils

Há duas maneiras de você fazer isto. Você pode usar os valores em um hash para uma tabela de consulta, como sugerido pelos outros posts. (Vou adicionar apenas outro idioma.)

 my %bad_param_lookup; @bad_param_lookup{ @bad_params } = ( 1 ) x @bad_params; 

Mas se são dados de caracteres em sua maioria de palavras e não de meta demais, você pode despejá-lo em uma alternância regex:

 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/; 

Essa solução teria que ser ajustada para os tipos de “valores ruins” que você está procurando. E, novamente, pode ser totalmente inapropriado para certos tipos de strings, portanto, evite o emptor .

@files é um array existente

 my @new_values = grep(/^2[\d].[\d][A-za-z]?/,@files); print join("\n", @new_values); print "\n"; 

/^2[\d].[\d][A-za-z]?/ = vaues a partir de 2 aqui você pode colocar qualquer expressão regular

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

Você pode querer verificar a consistência numérica dos espaços iniciais