estoy usando each
para iterar a través de un hash de Perl:¿Puedo copiar un hash sin reiniciar su iterador "cada"?
while (my ($key,$val) = each %hash) {
...
}
Entonces sucede algo muy interesante y quiero imprimir el hash. Al principio me considero algo como:
while (my ($key,$val) = each %hash) {
if (something_interesting_happens()) {
foreach my $k (keys %hash) { print "$k => $hash{$k}\n" }
}
}
Pero eso no va a funcionar, porque todo el mundo sabe que llamar keys
(o values
) en un hash restablece el iterador interno utilizado para each
, y podemos conseguir un bucle infinito. Por ejemplo, estos scripts se ejecutarán para siempre:
perl -e '%a=(foo=>1); while(each %a){keys %a}'
perl -e '%a=(foo=>1); while(each %a){values %a}'
No hay problema, pensé. Podría hacer una copia del hash e imprimir la copia.
if (something_interesting_happens()) {
%hash2 = %hash;
foreach my $k (keys %hash2) { print "$k => $hash2{$k}\n" }
}
Pero eso tampoco funciona. Esto también restablece el iterador each
. De hecho, cualquier uso de %hash
en un contexto de lista parece restablecer su iterador each
. Entonces estos también se ejecutan para siempre:
perl -e '%a=(foo=>1); while(each %a){%b = %a}'
perl -e '%a=(foo=>1); while(each %a){@b = %a}'
perl -e '%a=(foo=>1); while(each %a){print %a}'
¿Se ha documentado esto en algún lugar? Tiene sentido que perl necesite usar el mismo iterador interno para enviar los contenidos de un hash a una pila de retorno, pero también puedo imaginar implementaciones de hash que no necesitan hacer eso.
Más importante, ¿hay alguna manera de hacer lo que quiero? Para obtener todos los elementos de un hash sin restablecer el iterador each
?
Esto también sugiere que no se puede depurar un hash dentro de una iteración each
, tampoco. Consideran que ejecuta el depurador en:
%a = (foo => 123, bar => 456);
while (($k,$v) = each %a) {
$DB::single = 1;
$o .= "$k,$v;";
}
print $o;
Sólo inspeccionando el hash donde se detiene el depurador (por ejemplo, al escribir o p %a
x %a
), que va a cambiar la salida del programa.
Actualización: he subido Hash::SafeKeys
como una solución general a este problema. Gracias @gpojd por apuntarme en la dirección correcta y @cjm por una sugerencia que simplificó mucho la solución.
Encontré el mismo problema recientemente. Descubrí que Hash :: SafeKeys es bastante lento y depende del tamaño del hash, y que las teclas predeterminadas Hash :: StoredIterator + tienen un rendimiento mucho mejor. –
@AlexandrEvstigneev - gracias, esto es muy interesante. Voy a hacer algunas actualizaciones a 'Hash :: SafeKeys' – mob