Des codes sources perlConsultez toutes les FAQ
Nombre d'auteurs : 13, nombre de questions : 59, dernière mise à jour : 27 mai 2011
Il existe différentes méthodes pour supprimer les doublons dans une liste (un tableau) en perl. La première méthode
consiste à utiliser un module Perl. C'est bien évidemment la méthode la plus recommandée car les modules disponibles sur le CPAN sont efficaces et bien écrits.
Voici une liste de modules intéressants :
La seconde méthode revient à ne pas utiliser de modules et coder soit même. Voici 3 procédures élégantes qui permettent de supprimer les doublons d'un tableau. Comparons les.
- Technique 1 (hash anonyme)
Cette procédure prend en argument une référence d'un tableau et chaque case du tableau devient une clé d'un hash anonyme. Les clés de la table de hachage sont renvoyées.
#
!/usr/bin/perl
use strict;
use warnings;
sub
doublons_hash_anonyme
{
my ($ref_tabeau
) =
@_
;
return keys
%{
{
map
{
$
_
=
>
1
}
@
{
$
ref_tabeau
}
}
}
;
}
- Technique 2 (tranche)
Cette procédure prend en argument une référence d'un tableau et chaque case du tableau devient la clé d'une tranche de hachage d'un hachage qu'on aura déclaré.
#
!/usr/bin/perl
use strict;
use warnings;
sub
doublons_tranche
{
my ($ref_tabeau
) =
@_
;
my %hash_sans_doublon
; #
Comme
un
hash
ne
peut
pas
avoir
deux
clés
identiques,
#
utilser
ces
clé
permet
d'avoir
un
tableau
unique
@hash_sans_doublon
{
@{
$
ref_tabeau
}
}
=
(); #
Pas
besoin
de
surcharger
le
hash
avec
des
valeurs
inutiles
#
et
ensuite,
on
renvoie
le
tableau
des
clés
uniques
return keys
%hash_sans_doublon
;
}
- Technique 3 (grep)
Cette procédure prend en argument une référence d'un tableau et chaque case est passée au filtre grep qui ne conserve que celles qui n'ont pas déjà été vues.
#
!/usr/bin/perl
use strict;
use warnings;
sub
doublons_grep
{
my ($ref_tabeau
) =
@_
;
my %hash_sans_doublon
;
return grep
{
!
$hash_sans_doublon
{
$_
}
+
+
}
@{
$
ref_tabeau
}
;
}
- Avantages et inconvénients
Ces trois techniques sont simples, efficaces et facilement maintenables. Il n'est pas du tout nécessaire
d'installer un nouveau module pour cela, bien que List::Util soit
dans le CORE de perl depuis sa version 5.8.
Pour de plus amples manipulations de tableaux, je vois recommande biensûr les modules cités ci-dessus.
Sachez maintenant que parmi ces 3 procédures, certaines sont plus rapides que d'autres.
la technique 2 (à coup de tranche) est la plus rapide des 3, ensuite vient la technique 3
(à coup de grep), puis vient la dernière (technique 1). Ce n'est qu'une remarque,
mais tout dépend de la longueur des listes, du nombre de doublons qu'elles contiennent, etc ...
En général, la première place est toujours partagée entre la technique 2 (à coup de tranche) et 3 (à coup de grep).
Autres remarques :
- La technique 2 (tranche) est plus avantageuse que la 1 car elle n'a pas besoin de créer
une liste intermédiaire.
- Notez bien que seule la technique 3 ( grep) permet de conserver l'ordre des éléments
du tableau après suppression des doublons (important selon vos besoins) !
Sachez que la différence de temps entre la 2 et 3 n'est pas non plus dramatique.
Utilisez le module Benchmark pour effectuer vos tests si vous le souhaitez.
- Exemple d'utilisation
#
!/usr/bin/perl
use strict;
use warnings;
my @listing_mots
=
qw/
moi
2Eurocents
Woufeil
toi
djibril
jedai
moi
jedai
gldavid
etc
jedai
stoyak
lui
nous
tous
je
tu
elle
lui
perl
10
20
20
30
8
6
9
100
/
;
my @resultat1
=
doublons_hash_ano( \@listing_mots );
my @resultat2
=
doublons_tranche( \@listing_mots );
my @resultat3
=
doublons_grep( \@listing_mots );
print
"
Original
:
@
listing_mots
\n
"
;
print
"
HASH
anonyme
:
@
resultat1
\n
"
;
print
"
Tranche
:
@
resultat2
\n
"
;
print
"
GREP
:
@
resultat3
\n
"
;
sub
doublons_hash_anonyme
{
my ($ref_tabeau
) =
@_
;
return keys
%{
{
map
{
$
_
=
>
1
}
@
{
$
ref_tabeau
}
}
}
;
}
sub
doublons_grep
{
my ($ref_tabeau
) =
@_
;
my %hash_sans_doublon
;
return grep
{
!
$hash_sans_doublon
{
$_
}
+
+
}
@{
$
ref_tabeau
}
;
}
sub
doublons_tranche
{
my ($ref_tabeau
) =
@_
;
my %hash_sans_doublon
; #
Comme
un
hash
ne
peut
pas
avoir
deux
clés
identiques,
#
utilser
ces
clé
permet
d'avoir
un
tableau
unique
@hash_sans_doublon
{
@{
$
ref_tabeau
}
}
=
(); #
Pas
besoin
de
surcharger
le
hash
avec
des
valeurs
inutiles
#
et
ensuite,
on
renvoie
le
tableau
des
clés
uniques
return keys
%hash_sans_doublon
;
}
sub
doublons_hash_ano
{
my ($ref_tabeau
) =
@_
;
return keys
%{
{
map
{
$
_
=
>
1
}
@
{
$
ref_tabeau
}
}
}
;
}
Original : moi 2Eurocents Woufeil toi djibril jedai moi jedai gldavid etc jedai stoyak lui nous tous je tu elle lui perl 10 20 20 30 8 6 9 100
HASH anonyme : djibril toi je gldavid etc tu Woufeil 30 100 moi 6 jedai 9 20 8 2Eurocents perl tous lui stoyak elle 10 nous
Tranche : djibril toi je gldavid etc tu Woufeil 30 100 moi 6 jedai 9 20 8 2Eurocents perl tous lui stoyak elle 10 nous
GREP : moi 2Eurocents Woufeil toi djibril jedai gldavid etc stoyak lui nous tous je tu elle perl 10 20 30 8 6 9 100
On peut constater que l'ordre est conservé pour la technique 3. Voilà, à vos tests. :-) !!
La fonction sort() en Perl est très puissante et permet de faire tous les tris inimaginables.
Elle trie les éléments d'une liste et retourne une liste triée des éléments fournis en paramètre.
Ces deux listes peuvent être distinctes, ou bien la liste d'origine peut être écrasée par le résultat
du tri si elle est destinataire de l'affectation.
La fonction sort peut prendre un argument spécial qui est le bloc de comparaison
à effectuer entre les éléments de la liste à trier. Il est ainsi possible de trier
selon des critères tout à fait personnalisés.
Par défaut, le tri se fait dans l'ordre "lexicographique" (ordre alphabétique,
étendu aux nombres dont les chiffres sont traités comme des caractères et non
comme des nombres). Le bloc de comparaison personnalisé doit
être précisé entre accolades, avant la liste d'éléments à trier, sans être séparé
de celle-ci par une virgule. Pour la comparaison, ce bloc utilise deux variables
internes de PERL, $a et $b, qui ne sont définies que dans ce bloc et masquent
toute variable $a ou $b propre à l'utilisateur. Ce bloc effectue donc un test
quelconque, basé sur $a et $b, dont le résultat peut prendre trois valeurs :
- positif si $a est avant $b dans l'ordre de tri souhaité
- nul si $a et $b sont équivalents
- négatif si $a est après $b dans l'ordre souhaité.
Ces trois valeurs de résultat de test correspondent aux résultats des
opérateurs de test et cmp vus précédemment.
Ainsi, pour un tri lexicographique du tableau @t, on peut faire :
@l
=
sort
( @t
); ou bien @l
=
sort
( {
$a
cmp
$b
}
@t
);
qui a l'avantage d'être totalement explicite et donc plus clair à maintenir.
Pour un tri lexicographique inversé, on peut aussi bien écrire :
@l
=
reverse
( sort
( {
$a
cmp
$b
}
@t
) );
que
@l
=
sort
( {
$b
cmp
$a
}
@t
);
Mais sachez que reverse est beaucoup plus optimal que faire sort ( { $b cmp $a } @t.
Pour un tri numérique :
@l
=
sort
( {
$a
<=
>
$b
}
@t
);
Pour un tri un peu spécial, en supposant que @t contienne des indices d'un tableau et que l'on souhaite un tri de ces indices selon les valeurs numériques du tableau :
@l
=
sort
( {
$tableau
[$a
] <=
>
$tableau
[$b
] }
@t
);
@l contient alors les valeurs de @t triées dans un ordre tel qu'elles indicent des valeurs croissantes dans @tableau. C'est très indirect et ce n'est pas forcément très naturel au début, mais c'est extrêmement puissant. Et pour finir, bien que l'on ait pas encore abordé la définition de fonctions personnalisées, il est possible d'appeler une fonction définie par le programmeur :
@l
=
sort
( {
mafonction ($a
, $b
) }
@t
);
Nous vous recommandons de lire les articles ci dessous
les mongueurs de Perl (groupe francophone consacré à la promotion de Perl) ont écrit un très bon article.
En voici d'autres :
L'excellent article de Uri Guttman et Larry Rosler : A Fresh Look at Efficient Perl Sorting,
La traduction de cet article par votre serviteur les mongeurs est disponible ici.
Il arrive que l'on ait besoin de comparer 2 listes perl entre elles afin d'obtenir les données communes aux deux listes, les données présentes dans une liste mais pas dans l'autre ... Pas besoin de reinventer la roue, il existe un module sur le CPAN qui le fait très bien, c'est List::CompareList::Compare. Voici un exemple de code :
#
!/usr/bin/perl
use strict;
use warnings;
use List::Compare;
my @Llist
=
qw(
abel
abel
baker
camera
delta
edward
fargo
golfer
)
;
my @Rlist
=
qw(
baker
camera
delta
delta
edward
fargo
golfer
hilton
)
;
my $lc
=
List::Compare-
>
new( \@Llist, \@Rlist );
my @intersection
=
$lc
-
>
get_intersection;
my @union
=
$lc
-
>
get_union;
print
"
@
intersection
\n
@
union
\n
"
;
#
baker
camera
delta
edward
fargo
golfer
#
abel
baker
camera
delta
edward
fargo
golfer
hilton