2011-03-03 27 views
18

Me pregunto si Perl tiene una forma incorporada de verificar la existencia de un elemento hash con una clave que coincida con una expresión regular determinada. Por ejemplo:Perl: Coincidencia de claves hash con una expresión regular

my %h = ('twelve' => 12, 'thirteen' => 13, 'fourteen' => 14); 

Me pregunto si hay alguna manera de hacer esto:

print "We have 12\n" if exists $h{twelve}; 
print "We have some teens\n" if exists $h{/.*teen$/}; 
+2

Si tiene el 'teen $' el '. *' Es superfluo. – Axeman

Respuesta

24

El smart match operator hace esto (disponible desde Perl v5.10).

$a  $b  Type of Match Implied Matching Code 
====== =====  ===================== ============= 
... 
Regex Hash  hash key grep   grep /$a/, keys %$b 
... 

Ejemplo de uso:

# print if any key in %h ends in "teen" 
print "We have some teens\n" if /.*teen$/ ~~ %h; 
+1

Tenga en cuenta que este operador ha estado en un estado de flujo y se ha considerado "experimental" a partir de Perl 5.18 (emitiendo advertencias en cualquier lugar que se use). http://stackoverflow.com/questions/16927024/perl-5-20-and-the-fate-of-smartmatch-and-given-when/ – sundar

+1

Está funcionando bien para mí. – user1807271

6

Sí, se llama:

use List::Util qw<first>; 

# Your regex does not compile perhaps you mean /teen$/ 
my $value = $hash{ (first { m/teen/ } keys %hash) || '' }; 

(Antes del partido inteligente, es decir Véase la respuesta de turba para el partido inteligente..)

Usted también podría ordenar las claves:

my $value = $hash{ (first { m/teen/ } sort keys %hash) || '' }; 

Me congele esto en una "operación":

use Scalar::Util qw<reftype>; 

sub values_for_keys_like (\[%$]$) { 
    my $ref = reftype($_[0]) eq 'HASH' ? $_[0] : $$_[0]; 
    return unless my @keys = keys %$ref; 

    my $regex = shift; 
    # allow strings 
    $regex = qr/$regex/ unless my $typ = ref($regex); 
    # allow regex or just plain ol' filter functions. 
    my $test = $typ eq 'CODE' ? $regex : sub { return unless m/$regex/; 1 }; 

    if (wantarray) { 
     return unless my @k = grep { defined $test->($_) } @keys; 
     return @$ref{ @k }; 
    } 
    else { 
     return unless my $key = first { defined $test->($_) } @keys; 
     return $ref->{ $key }; 
    } 
} 

Y se podría utilizar de este modo:

my $key = values_for_keys_like(%hash => qr/teen/); 

O

my $key = values_for_keys_like($base->{level_two}{level_three} => qr/teen/); 
+0

'sort'? Eh? No hay ninguna razón para clasificar las claves si solo está probando si existe una coincidencia. Solo 'primero {m/teen $ /} keys% hash' es mejor. –

+0

@Dave Sherohman: Como es habitual en SO, está la pregunta general y está la aplicación particular que el usuario está buscando. La pregunta general es "Coincidencia de teclas hash con expresión regular". 'first' es una implementación del concepto' any', pero contiene otras implicaciones: primero * ¿cómo? * Tiendo a querer abarcar más que la aplicación específica. – Axeman

+2

No veo * ninguna * situación en la que la clasificación sea aplicable a la pregunta "¿hay alguna coincidencia?". Si una coincidencia existe o no es independiente del orden, el orden es irrelevante. El único efecto de la clasificación es imponer un orden específico, pero el orden es irrelevante, por lo que ordenar simplemente agota los recursos (tiempo/memoria) sin propósito. La pregunta aquí es preguntar sobre el concepto 'any', independientemente del orden; 'first' es apropiado únicamente porque' List :: Util' no proporciona una función 'any'. –

12

Además de las otras respuestas aquí también puedes hacer esto con grep de perl:

print "We have some teens\n" if grep {/.*teen/} keys %h; 
Cuestiones relacionadas