Desde hace unas semanas, la gente que se dedica a crear las nuevas versiones del intérprete de Perl (los Perl 5 Porters), decidieron cambiar el algoritmo de los
hashes, más bien, en el orden de la creación de los pares clave/valor.
Los
hash en Perl v5 ya se ordenaban de forma aleatoria, por un tema que tiene que ver con asuntos de seguridad: los
hash se pueden usar para temas de claves/autenticación, y en esos casos es vital que nadie, desde fuera del programa, pueda adivinar el orden de creación de esos pares.
Pero ahora han añadido un elemento de seguridad más: el orden de creación de los
hashes será diferente en cada ejecución del programa.
Entonces, ya que sabíamos que en nuestros programas no debíamos confiar en el orden de los pares clave/valor, ahora se junta la dificultad que tampoco debemos confiarnos en que en cada ejecución del programa, las claves y valores son devueltos siempre en el mismo orden.
A partir de Perl v5.17.6 se han empezado a recibir mensajes de error en muchos módulos de CPAN, precisamente por ese "pequeño" detalle.
Un ejemplo. Supongamos el siguiente trozo de código:
Using perl Syntax Highlighting
my $ops = join "|", map quotemeta, keys %hash;
my $regex = qr/^($ops)?($rest_of_regex)/;
Coloreado en 0.002 segundos, usando
GeSHi 1.0.8.4
¿Se ve el problema? La expresión regular con una alternativa viene derivada de las claves de un
hash. Al principio, parece que no es importante el orden de los miembros en la alternancia... excepto cuando un miembro es un prefijo de otro, y en ese caso el primero gana. Por ejemplo, buscando por "absurdo" con
qr/^(a|ab|abc)?(.*)/ no es lo mismo que hacerlo con
qr/^(abc|ab|a)?(.*)/: en un caso, $1 contendrá 'a', y en el otro caso, 'ab'.
Esto está provocando un revuelo tremendo en muchos autores de módulos, que tienen que revisar sus códigos, para evitar estos problemas. En la mayor parte de los casos, se puede resolver sacando las claves (y/o valores) en un determinado orden, con sort() o cualquier otro método de ordenación.
Como se indica en
perlsec, desde la versión v5.8.1 (año 2003), Perl nunca ha garantizado ninguna ordenación de las claves de un
hash, y de hecho la ordenación ha cambiado varias veces a lo largo de la historia. El problema es que muchos desarrolladores inadvertidamente acaban confiando en la ordenación por defecto de los
hash, o en un aleatorio, pero orden constante, simplemente porque el ese orden funcionaba en su máquina. Vamos... un error difícil de cazar...
Ejemplo. Supongamos el siguiente programa Perl de una línea:
Using bash Syntax Highlighting
perl -E 'local $,=q[, ]; $hash{$_} = $_ for 1..15; say keys %hash'
6, 11, 3, 7, 9, 12, 2, 15, 14, 8, 1, 4, 13, 10, 5
Coloreado en 0.002 segundos, usando
GeSHi 1.0.8.4
¿Qué sucede si el código añade una decimosexta clave y, entonces, como si corrigiéramos un error, la volvemos a quitar? Siguen estando quince claves, las mismas claves que antes, pero, estarán en el mismo orden, ¿no? Pues no:
Using bash Syntax Highlighting
perl -E 'local $,=q[, ]; $hash{$_}=$_ for 1..15; $hash{16}=16; delete $hash{16}; say keys %hash'
11, 7, 2, 1, 13, 6, 3, 9, 12, 14, 15, 8, 4, 10, 5
Coloreado en 0.001 segundos, usando
GeSHi 1.0.8.4
Esto sucede siempre, como cuando reutilizamos una variable
hash:
Using perl Syntax Highlighting
sub init { ( 1=>1, 2=>2, 3=>3, 4=>4, 5=>5 ) }
my %hash = init();
say "original: " . join ', ' => keys %hash;
$hash{$_} = $_ for 6..100;
%hash = init(); # recupera los valores originales
say "original? " . join ', ' => keys %hash;
Coloreado en 0.001 segundos, usando
GeSHi 1.0.8.4
Esto es lo que se obtiene en un antiguo Perl v5.14.3:
Using text Syntax Highlighting
original: 4, 1, 3, 2, 5
original? 2, 1, 3, 4, 5
Coloreado en 0.000 segundos, usando
GeSHi 1.0.8.4
Como se ve, esto es un problema real y puede acechar en cualquier código nuestro. El parche que se ha puesto en los Perl de desarrollo (ahora v5.17) es mostrar este comportamiento de una forma más explícita. Esto es bueno, porque aparte de añadir una protección extra en temas de seguridad, mostrará código erróneo de forma más fácil. Si ejecutamos el código anterior con la versión v5.17.6 obtendremos un orden de las claves diferente en cada ejecución:
Using bash Syntax Highlighting
perl -E 'local $,=q[, ]; $hash{$_} = $_ for 1..15; say keys %hash'
1, 5, 15, 12, 6, 4, 10, 9, 3, 13, 7, 14, 11, 2, 8
perl -E 'local $,=q[, ]; $hash{$_} = $_ for 1..15; say keys %hash'
5, 11, 7, 3, 15, 6, 12, 2, 13, 9, 8, 14, 10, 1, 4
perl -E 'local $,=q[, ]; $hash{$_} = $_ for 1..15; say keys %hash'
2, 15, 14, 13, 5, 1, 9, 10, 3, 11, 6, 8, 12, 4, 7
Coloreado en 0.001 segundos, usando
GeSHi 1.0.8.4
Así que ya sabéis: hay que repasar nuestro código, sobre todo cuando usemos las funciones keys(), values() y each().