¿Cómo puedo verificar si una matriz Perl contiene un valor particular?

Estoy tratando de encontrar una manera de verificar la existencia de un valor en una matriz sin iterar a través de la matriz.

Estoy leyendo un archivo para un parámetro. Tengo una larga lista de parámetros con los que no quiero lidiar. @badparams estos parámetros no deseados en una matriz @badparams .

Quiero leer un nuevo parámetro y si no existe en @badparams , procesarlo. Si existe en @badparams , vaya a la siguiente lectura.

Simplemente convierta la matriz en un hash:

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

También puede agregar más parámetros (únicos) a la lista:

 $params{$newparam} = 1; 

Y luego obtener una lista de params (únicos) de vuelta:

 @badparams = keys %params; 

El mejor propósito general: matrices especialmente cortas (1000 elementos o menos) y codificadores que no están seguros de qué optimizaciones se adaptan mejor a sus necesidades.

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

Se ha mencionado que grep pasa a través de todos los valores incluso si el primer valor en la matriz coincide. Esto es cierto, sin embargo, grep sigue siendo extremadamente rápido para la mayoría de los casos . Si está hablando de arreglos cortos (menos de 1000 elementos), la mayoría de los algoritmos van a ser bastante rápidos de todos modos. Si está hablando de arreglos muy largos (1,000,000 de elementos) grep es aceptablemente rápido independientemente de si el elemento es el primero o el medio o el último en la matriz.

Casos de optimización para matrices más largas:

Si su matriz está ordenada , use una “búsqueda binaria”.

Si la misma matriz se busca repetidamente muchas veces, primero cópiela en un hash y luego verifique el hash. Si la memoria es una preocupación, mueva cada elemento de la matriz al hash. Más eficiente con la memoria pero destruye la matriz original.

Si se buscan los mismos valores repetidamente dentro de la matriz, cree perezosamente una caché. (a medida que se busca cada elemento, compruebe primero si el resultado de la búsqueda se almacenó en un hash persistente. Si el resultado de la búsqueda no se encuentra en el hash, busque el arreglo y coloque el resultado en el hash persistente para que la próxima vez lo hagamos encuéntralo en el hash y omite la búsqueda).

Nota: estas optimizaciones solo serán más rápidas cuando se trate de matrices largas. No optimice en exceso.

Puede usar la función Smartmatch en Perl 5.10 de la siguiente manera:

Para la búsqueda de valor literal que sigue a continuación, hará el truco.

 if ( "value" ~~ @array ) 

Para la búsqueda escalar, hacer lo siguiente funcionará como se indicó anteriormente.

 if ($val ~~ @array) 

Para una matriz en línea haciendo abajo, funcionará como se indicó anteriormente.

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

En Perl 5.18, Smartmatch se marca como experimental, por lo tanto, debe desactivar las advertencias activando pragma experimental agregando a continuación su secuencia de comandos / módulo:

 use experimental 'smartmatch'; 

Alternativamente, si desea evitar el uso de smartmatch, entonces, como dijo Aaron, use:

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

Esta publicación de blog analiza las mejores respuestas a esta pregunta.

Como resumen, si puede instalar módulos CPAN, las soluciones más legibles son:

 any(@ingredients) eq 'flour'; 

o

 @ingredients->contains('flour'); 

Sin embargo, una expresión más común es:

 any { $_ eq 'flour' } @ingredients 

¡Pero por favor no use la first() función first() ! No expresa la intención de su código en absoluto. No use el operador ~~ “Smart match”: está roto. Y no use grep() ni la solución con hash: iteran por toda la lista.

any() se detendrá tan pronto como encuentre su valor.

Consulte la publicación del blog para más detalles.

A pesar de que es conveniente de usar, parece que la solución de convertir a hash cuesta bastante rendimiento, lo cual fue un problema para mí.

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

Salida de prueba de referencia:

 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) 

El punto de referencia de @ eakssjo está roto: mide la creación de hash en bucle vs la creación de expresiones regulares en bucle. Versión fija (además he agregado List::Util::first y 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} ); }, }); 

Y resultado (es para 100_000 iteraciones, diez veces más que en la respuesta 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 (puede ser cuidadoso si se espera que el valor sea una expresión regular).

Intenta evitar el uso de grep , si buscas recursos.

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

Método 2: búsqueda lineal

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

Método 3: Usa un hash

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

Método 4: Smartmatch

(agregado en Perl 5.10, marcado es experimental en Perl 5.18).

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

Método 5: utilice la List::MoreUtils módulo principal List::MoreUtils

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

Ciertamente quieres un hash aquí. Coloque los parámetros incorrectos como claves en el hash, luego decida si existe un parámetro particular en el hash.

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

Si realmente está interesado en hacerlo con una matriz, consulte List::Util o List::MoreUtils

hay dos maneras de hacer esto. Puede usar arrojar los valores en un hash para una tabla de búsqueda, como lo sugieren las otras publicaciones. (Añadiré solo otro modismo).

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

Pero si se trata de datos de caracteres en su mayoría de palabras y no demasiados meta, puedes volcarlos en una alternancia de expresiones regulares:

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

Esta solución debería ajustarse para los tipos de “valores incorrectos” que está buscando. Y de nuevo, podría ser totalmente inapropiado para ciertos tipos de cadenas, así que caveat emptor .

@files es una matriz 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 aquí puede poner cualquier expresión 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"; } } 

Es posible que desee comprobar la consistencia de los espacios principales digitales numéricos