En este articulo veremos como podemos ordenar arrays de strings o números en Perl.
Perl incluye la función sort
que sirve para ordenar un array. En su forma más sencilla
le das un array y devuelve los elementos de ese array ordenados. @sorted = sort @original
.
Ordenar en ASCII
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
use Data::Dumper qw(Dumper);
my @words = qw(foo bar zorg moo);
say Dumper \@words;
my @sorted_words = sort @words;
say Dumper \@sorted_words;
El ejemplo anterior mostrará
$VAR1 = [
'foo',
'bar',
'zorg',
'moo'
];
$VAR1 = [
'bar',
'foo',
'moo',
'zorg'
];
En primer lugar tenemos el contenido del array antes de ordenarlo, después el contenido en orden.
Este es el caso más simple, pero no es lo que queremos siempre. Por ejemplo, ¿que pasa si algunas de las palabras empiezan con letras mayúsculas?
my @words = qw(foo bar Zorg moo);
El contenido en @sorted_words
será:
$VAR1 = [
'Zorg',
'bar',
'foo',
'moo'
];
Como ves, la palabra que empieza con una letra mayúscula aparece primero.
Esto es porque sort
ordena por defecto usando la tabla ASCII, ahí todas
las letras mayúsculas aparecen antes que las minúsculas.
Funciones de comparación
sort
funciona en Perl de la siguiente manera: recorre los elementos del array
y en cada turno pone el primer elemento en la variable $a
y el segundo en la
variable $b
. Entonces llama a la función de comparación. Esta función
devolverá 1 si el contenido de $a
debería estar a la izquierda, -1 si el contenido
de $b
debería estar a la izquierda, o 0 si no importa porque los dos valores son
iguales.
Por defecto no ves esta función de comparación y sort compara los valores usando la tabla ASCII, se puede escribir de forma explicita:
sort { $a cmp $b } @words;
Este código dará exactamente el mismo resultado que el código: sort @words
.
Puedes ver que, por defecto, se usa cmp
como función de comparación.
Lo que hace cmp es comparar los valores de ambos lados como strings, devuelve 1
si el argumento de la izquierda es "menor que" el de la derecha, devuelve -1 si
el argumento de la izquierda es "mayor que" el de la derecha, y devuelve 0 si son
iguales.
Usando un orden alfabético
Si quieres usar un orden alfabético, ignorando mayúsculas y minúsculas, puedes hacerlo así:
my @sorted_words = sort { lc($a) cmp lc($b) } @words;
Usamos lc
que devuelve la versión en minúsculas (lower case) del valor pasado
como parámetro.
Después cmp
compara estas versiones en minúsculas y decide donde colocar los
strings originales.
El contenido en @sorted_words
será:
$VAR1 = [
'bar',
'foo',
'moo',
'Zorg'
];
Ordenando números en Perl
Si rellenamos un array con números y lo ordenamos usando el orden por defecto, el resultando no será el que probablemente estamos esperando.
my @numbers = (14, 3, 12, 2, 23);
my @sorted_numbers = sort @numbers;
say Dumper \@sorted_numbers;
$VAR1 = [
12,
14,
2,
23,
3
];
Por supuesto si lo piensas no es tan sorprendente. Cuando la función de comparación ve 12 y 3, los compara como strings. Por lo tanto, comparará el primer carácter de cada string, "1" y "3". Como "1" esta antes que "3" en la tabla ASCII, el string "12" vendrá antes que el string "3".
Perl no adivina mágicamente que quieres ordenar estos valores como números.
De todas formas no hay problema, podemos escribir una función que compare los dos valores como
números.
Para ello usamos el operador
my @sorted_numbers = sort { $a <=> $b } @numbers;
Devolverá el contenido ordenado así:
$VAR1 = [
2,
3,
12,
14,
23
];
Puedes ver algunos ejemplos más en ordenando strings mixtos (en).