Aquí podría ser tu PUBLICIDAD


¿Cuál es la forma más segura de iterar a través de las teclas de un hash Perl?

votos
84

Si tengo un hash Perl con un grupo de pares (clave, valor), ¿cuál es el método preferido para iterar a través de todas las claves? He oído que el uso eachpuede de alguna manera tener efectos secundarios involuntarios. Entonces, ¿es cierto, y es uno de los dos métodos siguientes mejores, o hay una mejor manera?

# Method 1
while (my ($key, $value) = each(%hash)) {
    # Something
}

# Method 2
foreach my $key (keys(%hash)) {
    # Something
}
Publicado el 06/08/2008 a las 03:53
fuente por usuario Rudd Zwolinski
En otros idiomas...        العربية       

9 respuestas

votos
170

La regla de oro es usar la función más adecuada a sus necesidades.

Si solo quiere las claves y no planea leer ninguno de los valores, use las teclas ():

foreach my $key (keys %hash) { ... }

Si solo quiere los valores, use values ​​():

foreach my $val (values %hash) { ... }

Si necesita las claves y los valores, use cada uno ():

keys %hash; # reset the internal iterator so a prior each() doesn't affect the loop
while(my($k, $v) = each %hash) { ... }

Si planea cambiar las claves del hash de cualquier manera, excepto para eliminar la clave actual durante la iteración, entonces no debe usar cada (). Por ejemplo, este código para crear un nuevo conjunto de claves en mayúscula con valores duplicados funciona bien usando las teclas ():

%h = (a => 1, b => 2);

foreach my $k (keys %h)
{
  $h{uc $k} = $h{$k} * 2;
}

produciendo el hash resultante esperado:

(a => 1, A => 2, b => 2, B => 4)

Pero usando cada () para hacer lo mismo:

%h = (a => 1, b => 2);

keys %h;
while(my($k, $v) = each %h)
{
  $h{uc $k} = $h{$k} * 2; # BAD IDEA!
}

produce resultados incorrectos en formas difíciles de predecir. Por ejemplo:

(a => 1, A => 2, b => 2, B => 8)

Esto, sin embargo, es seguro:

keys %h;
while(my($k, $v) = each %h)
{
  if(...)
  {
    delete $h{$k}; # This is safe
  }
}

Todo esto se describe en la documentación de perl:

% perldoc -f keys
% perldoc -f each
Respondida el 06/08/2008 a las 02:22
fuente por usuario John Siracusa


Aquí podría ser tu PUBLICIDAD


votos
21

Una cosa que deberías saber al usar eaches que tiene el efecto secundario de agregar "estado" a tu hash (el hash tiene que recordar lo que es la "próxima" clave). Al usar código como los fragmentos publicados anteriormente, que iteran sobre el hash completo de una vez, esto no suele ser un problema. Sin embargo, se encontrará con problemas difíciles de rastrear (hablo por experiencia;), cuando se usa eachjunto con declaraciones como lasto returnpara salir del while ... eachciclo antes de haber procesado todas las claves.

En este caso, el hash recordará qué teclas ya ha devuelto, y cuando lo use eachla próxima vez (tal vez en una pieza de código totalmente no relacionada), continuará en esta posición.

Ejemplo:

my %hash = ( foo => 1, bar => 2, baz => 3, quux => 4 );

# find key 'baz'
while ( my ($k, $v) = each %hash ) {
    print "found key $k\n";
    last if $k eq 'baz'; # found it!
}

# later ...

print "the hash contains:\n";

# iterate over all keys:
while ( my ($k, $v) = each %hash ) {
    print "$k => $v\n";
}

Esto imprime:

found key bar
found key baz
the hash contains:
quux => 4
foo => 1

¿Qué pasó con las teclas "bar" y "baz"? Todavía están allí, pero el segundo eachcomienza donde el primero lo dejó, y se detiene cuando llega al final del hash, por lo que nunca los vemos en el segundo ciclo.

Respondida el 16/09/2008 a las 12:37
fuente por usuario 8jean

votos
19

El lugar donde eachpuede causarte problemas es que es un iterador verdadero sin ámbito. A modo de ejemplo:

while ( my ($key,$val) = each %a_hash ) {
    print "$key => $val\n";
    last if $val; #exits loop when $val is true
}

# but "each" hasn't reset!!
while ( my ($key,$val) = each %a_hash ) {
    # continues where the last loop left off
    print "$key => $val\n";
}

Si necesita asegurarse de eachobtener todas las claves y valores, debe asegurarse de utilizar keyso valuesprimero (ya que restablece el iterador). Vea la documentación de cada uno .

Respondida el 16/09/2008 a las 03:35
fuente por usuario Darren Meyer

votos
13

El uso de cada sintaxis evitará que todo el conjunto de claves se genere a la vez. Esto puede ser importante si está utilizando un hash vinculado a una base de datos con millones de filas. No desea generar la lista completa de claves a la vez y agotar su memoria física. En este caso, cada uno sirve como un iterador, mientras que las claves realmente generan toda la matriz antes de que comience el ciclo.

Entonces, el único lugar donde "cada" es de uso real es cuando el hash es muy grande (en comparación con la memoria disponible). Eso solo sucederá cuando el hash en sí no viva en la memoria, a menos que esté programando un dispositivo portátil de recolección de datos o algo con poca memoria.

Si la memoria no es un problema, generalmente el paradigma de mapa o claves es el paradigma más fácil de leer y más fácil de leer.

Respondida el 11/09/2008 a las 11:04
fuente por usuario Jeffrey Horn

votos
5

Algunos pensamientos misceláneos sobre este tema:

  1. No hay nada inseguro sobre cualquiera de los iteradores hash ellos mismos. Lo que no es seguro es modificar las claves de un hash mientras lo itera. (Es perfectamente seguro modificar los valores). El único efecto secundario potencial que puedo pensar es que valuesdevuelve alias, lo que significa que modificarlos modificará el contenido del hash. Esto es por diseño, pero puede no ser lo que desea en algunas circunstancias.
  2. La respuesta aceptada de John es buena, con una excepción: la documentación es clara de que no es seguro agregar claves mientras se itera sobre un hash. Puede funcionar para algunos conjuntos de datos pero fallará para otros dependiendo de la orden de hash.
  3. Como ya se indicó, es seguro eliminar la última clave devuelta por each. Esto es no cierto para los keysque eaches un iterador mientras que keysdevuelve una lista.
Respondida el 15/09/2008 a las 10:36
fuente por usuario Michael Carman

votos
4

Siempre uso el método 2 también. El único beneficio de usar cada uno es que si solo está leyendo (en lugar de volver a asignar) el valor de la entrada hash, no está constantemente desreferenciando el hash.

Respondida el 06/08/2008 a las 05:04
fuente por usuario jaredg

votos
4

Puedo ser mordido por este, pero creo que es una preferencia personal. No puedo encontrar ninguna referencia en los documentos para que cada () sea diferente de las teclas () o los valores () (aparte de la respuesta obvia de "devuelven cosas diferentes". De hecho, los documentos indican el uso del mismo iterador y todos devuelve los valores reales de la lista en lugar de copias de ellos, y que modificar el hash mientras se itera sobre él utilizando cualquier llamada es malo.

Dicho todo esto, casi siempre uso keys () porque para mí es más documentado para acceder al valor de la clave a través del hash. De vez en cuando uso valores () cuando el valor es una referencia a una estructura grande y la clave para el hash ya estaba almacenada en la estructura, momento en el que la clave es redundante y no la necesito. Creo que he usado cada uno () 2 veces en 10 años de programación de Perl y que probablemente fue la opción incorrecta ambas veces =)

Respondida el 06/08/2008 a las 04:43
fuente por usuario jj33

votos
1

Usualmente uso keysy no puedo recordar la última vez que usé o leí un uso de each.

¡No te olvides map, dependiendo de lo que estés haciendo en el ciclo!

map { print "$_ => $hash{$_}\n" } keys %hash;
Respondida el 22/08/2008 a las 04:31
fuente por usuario Gary Richardson

votos
-2

Yo digo woudl:

  1. Utilice lo que es más fácil de leer / entender para la mayoría de la gente (por lo que las llaves, por lo general, yo diría)
  2. Uso lo que decida consistentemente throught todo el código base.

Esto dará 2 grandes ventajas:

  1. Es más fácil de detectar código "común" para que pueda volver a factores en funciones / methiods.
  2. Es más fácil para los futuros desarrolladores de mantener.

No creo que es más caro de utilizar claves sobre cada uno, así que no hay necesidad de dos construcciones diferentes para la misma cosa en el código.

Respondida el 20/12/2010 a las 01:46
fuente por usuario Hogsmill