perl - ¿Por qué las teclas hash tienen un orden diferente al imprimir?
sorting data-structures (3)
Al imprimir un hash hay algunas nociones de orden diferentes que son relevantes: "orden de inserción", "orden de clasificación" y "aleatorio". Consulte la sección MEDIO AMBIENTE de la documentación perlrun
para ver una explicación de las formas en que puede controlar este comportamiento y las razones por las cuales el valor predeterminado es utilizar la asignación aleatoria de hash.
Durante al menos una década hashes en perl no han garantizado el orden de las claves. Más recientemente, la aleatorización hash ha sido parte de un esfuerzo general de "endurecimiento" de la seguridad. Hay buenas razones para hashes para ser aleatorizados. Para obtener más detalles, consulte la discusión de perlsec
sobre los ataques de complejidad algorítmica . Notará en la documentación de seguridad de Perl que se agregaron más mejoras en perl-5.18
: si observa un comportamiento diferente en comparación con las versiones anteriores, puede deberse a estos cambios más recientes.
Además de ordenar explícitamente sus claves hash de una manera determinista, hay otros enfoques que puede tomar para ordenar sus valores hash: Hash::Ordered
es un ejemplo. La documentación de Hash::Ordered
tiene una buena discusión de los pros y los contras de varios otros módulos.
Mientras que un hash es una " canasta desordenada " de escalares dispuestos en pares clave-valor; una matriz es una " secuencia ordenada " de escalares [ 1 ] . Un " corte " es la forma de acceder a "varios elementos de una lista, una matriz o un hash simultáneamente". Una porción usa @
sigil ya que la operación devuelve una lista de valores múltiples, y con @
obtenemos "secuencia ordenada". El resultado es que una manera de imponer un tipo de "orden" en un hash es mediante el uso de un sector para acceder a él:
# We want alphabetical disorder ...
my %hashed = ( 1 => "z", 2 => "x", 3 => "y" );
for my $key ( keys %hashed ) { print $hashed{$key} } ;
__END__
zyx
Queremos que " zxy
" no " zyx
". Para imponer nuestra versión arbitraria de orden en este hash, primero debemos reconocer que el culpable aquí es keys %hashed
que devuelve las claves en orden aleatorio. La solución es sort
claves de ccurse y en este ejemplo artificial las almacenamos en @sort_order
y lo usamos para "cortar" lo que queremos del hash, de la manera que lo deseamos:
my @sort_order = sort keys %hashed ;
print @hashed{@sort_order} ;
__END__
zxy
Tada !! Las divisiones pueden ser útiles cuando desee almacenar claves y valores en un hash, pero acceda a esos datos de forma ordenada. Recuerde la " @
" cuando quiera cortar un hash; como perldata
dice perldata
: "usas una ''@''
... en una porción hash ... [porque] estás volviendo ... una lista". Y las listas son ordenadas.
[ 1 ] Las definiciones de hash como "cestas desordenadas" y matrices como "secuencia ordenada" provienen del excelente artículo de Mike Friedman (FRIEDO) sobre Arrays vs. Listas en Perl .
Referencias adicionales
- cf.
perlfaq
-q ¿Cómo puedo mantener siempre mi hash ordenado? - Además de crear una cantidad de módulos de CPAN realmente útiles, GARU (Breno de Oliveira) publicó un excelente artículo sobre ordenamiento de hash que cubre exhaustivamente los problemas recientes de desarrollo de Perl y de aleatorización de hash.
- Para ejemplos más avanzados de cosas ordenadas que puede hacer con rebanadas de hash, consulte el artículo de Vince Veselosky. Las rebanadas de hash pueden reemplazar los bucles .
Quiero construir varios hash con las mismas teclas y que las claves tengan el mismo orden cuando las imprimo. Por lo tanto, en el siguiente ejemplo, las claves de $hash1
y $hash2
siempre deben tener el mismo orden, pero no debería haber necesidad de mantener ese orden al crear el hash.
use Data::Dumper;
my $hash1 = {
keyc => 2,
key1 => 1,
keya => 3,
keyb => 4,
};
my $hash2 = {
keyc => 2,
key1 => 1,
keya => 3,
keyb => 4,
};
print Dumper $hash1, $hash2;
Pero el resultado es el siguiente:
$VAR1 = {
''key1'' => 1,
''keyc'' => 2,
''keyb'' => 4,
''keya'' => 3
};
$VAR2 = {
''keyb'' => 4,
''keya'' => 3,
''keyc'' => 2,
''key1'' => 1
};
es decir, los hash tienen un orden diferente e inesperado. ¿Qué pasa con mi perl?
Mi versión perl es:
This is perl 5, version 18, subversion 2 (v5.18.2) built for darwin-thread-multi-2level
(with 2 registered patches, see perl -V for more detail)
Aviso: sé que las claves de perl hash son orden desordenado. Quiero que tengan el mismo orden, pero no debería haber necesidad de tener el orden ordenado. Espero que pueda obtener la misma salida de impresión si ejecuto el código nuevamente.
Siguiendo el consejo de las respuestas, establecí dos variables de entorno:
PERL_HASH_SEED=0x00 PERL_PERTURB_KEYS=0
Entonces puedo obtener el mismo resultado cuando ejecuto el código repetidamente.
La respuesta de G. Cito es correcta. Sin embargo, si desea una salida ordenada de Data :: Dumper, puede hacer lo siguiente:
use Data::Dumper;
my $hash1 = {
keyc => 2,
key1 => 1,
keya => 3,
keyb => 4,
};
my $hash2 = {
keyc => 2,
key1 => 1,
keya => 3,
keyb => 4,
};
my $dumper = Data::Dumper->new([$hash1, $hash2]);
$dumper->Sortkeys(1);
print $dumper->Dump;
No hay nada malo con su perl, un hash no está ordenado.
Si desea ordenar por clave, debe hacer algo como eso:
foreach my $key (sort keys %hash1) {
print $key, $hash1{$key};
}
y lo mismo para hash2 ...