2009-04-06 19 views
20

Tengo una lista de valores posibles:¿Cómo puedo verificar que un valor esté presente en una matriz (lista) en Perl?

@a = qw(foo bar baz); 

¿Cómo puedo comprobar de una manera concisa que un valor $val está presente o ausente en @a?

Una implementación obvia es recorrer la lista, pero estoy seguro TMTOWTDI.


Gracias a todos los que respondieron! Las tres respuestas que me gustaría destacar son:

  1. La respuesta aceptada - el más "integrado" y de manera compatible con versiones anteriores.

  2. RET's answer es el más limpio, pero solo es bueno para Perl 5.10 y posterior.

  3. draegtun's answer es (posiblemente) un poco más rápido, pero requiere el uso de un módulo adicional. No me gusta agregar dependencias si puedo evitarlas, y en este caso no necesito la diferencia de rendimiento, pero si tiene una lista de 1,000,000 de elementos, puede intentar probar esta respuesta.

+1

no estoy seguro de que veo la cuestión de la dependencia con la lista :: Util. Es estándar con Perl y si lo usa con qw/first/(como lo hizo Draegtun), solo importa la única subrutina. – Telemachus

+0

No es un problema per se, es más una preferencia personal. – MaxVT

+1

No hay un problema de dependencia con la respuesta List :: Util. Si fuera yo, esa sería la respuesta aceptada. La falta de voluntad de usar módulos básicos me parece una preferencia arraigada en la superstición. En este caso, grep {} es casi igual de bueno. – singingfish

Respuesta

20

de Perl en grep() función está diseñada para hacer esto.

@matches = grep(/^MyItem$/, @someArray); 

o puede insertar cualquier expresión en el matcher

@matches = grep($_ == $val, @a); 
+0

+1 ¡Hola! Simple y agradable =) Gracias – Viet

8

Un posible enfoque es utilizar la función List :: MoreUtils 'any'.

use List::MoreUtils qw/any/; 

my @array = qw(foo bar baz); 

print "Exist\n" if any {($_ eq "foo")} @array; 

actualización: corregido basado en el comentario de Zoul.

+0

Definido ("foo") siempre es cierto, ¿Se refería a $ _ eq 'foo'? – zoul

+0

-1 lo siento, por lo que Zoul mencionó. –

+0

Sí, necesita ser {$ _eq "foo"} o cualquier {m/^ foo \ z /} ... – jettero

5

solución interesante, sobre todo para la búsqueda repetida: Bulit

my %hash; 
map { $hash{$_}++ } @a; 
print $hash{$val}; 
+0

La sugerencia de Zoul acerca de un hash es bastante cercana a la óptima; sin embargo, sugiero que a medida que agregue y elimine valores de su matriz durante el transcurso de su programa agregue y elimine esos valores en su hash. –

+0

Además, aunque esto funciona y es común, algunas personas (incluido yo mismo, supongo) se quejarán de usar el mapa en un contexto vacío. ¿Por qué no $ hash {$ _} ++ para @a en su lugar? – jettero

+0

Sí, eso es más agradable. – zoul

38

Si tiene Perl 5.10, utilice el smart-match operator ~~

print "Exist\n" if $var ~~ @array;

Es casi mágico.

+0

NB. A partir de 5.10.1 la semántica ha cambiado un poco y debe ser: 'if $ var ~~ @ array'. Para ayudar, pienso en '~~' como 'in'. ref: http://perldoc.perl.org/perldelta.html#Smart-match-changes – draegtun

+0

Gracias - hemos cambiado el orden en consecuencia. – RET

+1

Mi URL de Perldoc ya no es válida. Aquí hay uno fijo: http://search.cpan.org/~dapm/perl-5.10.1/pod/perl5101delta.pod#Smart_match_changes – draegtun

2
$ perl -e '@a = qw(foo bar baz);$val="bar"; 
if (grep{$_ eq $val} @a) { 
    print "found" 
} else { 
    print "not found" 
}' 

encontró

$val='baq'; 

que no se encuentra

15

Utilice la función primera de List::Util que viene de serie con Perl ....

use List::Util qw/first/; 

my @a = qw(foo bar baz); 
if (first { $_ eq 'bar' } @a) { say "Found bar!" } 

NB.primero devuelve el primer elemento que encuentra y por lo tanto no tiene que iterar a través de la lista completa (que es lo que hará grep).

+1

por foolishbrat, si utilizo un sub importado, preferiría List :: MoreUtil :: any() porque el concepto ("Devuelve un valor verdadero si cualquier elemento en LIST cumple con el criterio") es semánticamente una mejor coincidencia con la pregunta que first() ("devuelve el primer elemento donde el resultado de BLOCK es un valor verdadero. ") – nohat

+0

Lista :: Util :: first() tiene la ventaja de ser un módulo principal (es decir, omnipresente). Si estuviera buscando opciones de CPAN, consideraría seriamente Perl6 :: Junction :: any ... if (any (@a) eq 'baz') {} – draegtun

18

Esto se responde en la respuesta perlfaq4 al "How can I tell whether a certain element is contained in a list or array?".

Para buscar en perlfaq, puede buscar en la lista de todas las preguntas en perlfaq usando su navegador favorito.

Desde la línea de comandos, puede usar el modificador -q a perldoc para buscar palabras clave. Se podría haber encontrado su respuesta mediante la búsqueda de "lista":

perldoc -q list 

(porciones de esta respuesta aportada por Anno Siegel y Brian D Foy)

oír la palabra "in" es una indicación que probablemente debiste haber usado un hash, no una lista o matriz, para almacenar tus datos. Los hashes están diseñados para responder a esta pregunta de manera rápida y eficiente. Las matrices no son

Dicho esto, hay varias maneras de abordar esto. En Perl 5.10 y posterior, puede utilizar el operador partido inteligente para comprobar que un artículo está contenido en una matriz o un hash:

use 5.010; 

if($item ~~ @array) 
    { 
    say "The array contains $item" 
    } 

if($item ~~ %hash) 
    { 
    say "The hash contains $item" 
    } 

Con versiones anteriores de Perl, que tiene que hacer un poco más de trabajo. Si se va a realizar esta consulta muchas veces en los valores de cadena arbitrarias, la manera más rápida es, probablemente, para invertir la matriz original y mantener un hash cuyas claves son los valores de la primera matriz:

@blues = qw/azure cerulean teal turquoise lapis-lazuli/; 
%is_blue =(); 
for (@blues) { $is_blue{$_} = 1 } 

Ahora se puede comprobar si $ is_blue {$ some_color}. Podría haber sido una buena idea mantener el blues todo en un hash en primer lugar.

Si los valores son enteros pequeños, puede usar una matriz indexada simple. Este tipo de una matriz tendrá menos espacio:

@primes = (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31); 
@is_tiny_prime =(); 
for (@primes) { $is_tiny_prime[$_] = 1 } 
# or simply @istiny_prime[@primes] = (1) x @primes; 

Ahora compruebe si is_tiny_prime $ [$ some_number].

Si los valores en cuestión son números enteros en lugar de cadenas, se puede ahorrar un montón de espacio mediante el uso de cadenas de bits en su lugar:

@articles = (1..10, 150..2000, 2017); 
undef $read; 
for (@articles) { vec($read,$_,1) = 1 } 

Ahora compruebe si vec ($ leer, $ n, 1) es cierto para algunos $ n.

Estos métodos garantizan pruebas individuales rápidas pero requieren una reorganización de la lista o matriz original. Solo pagan si tiene que probar varios valores con la misma matriz.

Si solo está probando una vez, el módulo estándar List :: Util exporta la función primero para este fin. Funciona deteniéndose una vez que encuentra el elemento. Está escrito en C para la velocidad, y su equivalente en Perl se parece a esta subrutina:

sub first (&@) { 
    my $code = shift; 
    foreach (@_) { 
     return $_ if &{$code}(); 
    } 
    undef; 
} 

Si la velocidad es de poco interés, el lenguaje común utiliza grep en contexto escalar (que devuelve el número de elementos que transmiten su condición) atravesar la lista completa. Sin embargo, esto tiene el beneficio de decirle cuántas coincidencias encontró.

my $is_there = grep $_ eq $whatever, @array; 

Si realmente desea extraer los elementos coincidentes, simplemente use grep en el contexto de la lista.

my @matches = grep $_ eq $whatever, @array; 
1

Si no te gusta dependencia innecesaria, implementar any o first mismo

sub first (&@) { 
    my $code = shift; 
    $code->() and return $_ foreach @_; 
    undef 
} 

sub any (&@) { 
    my $code = shift; 
    $code->() and return 1 foreach @_; 
    undef 
} 
Cuestiones relacionadas