Discussion:
Hash "réciproque" d'une liste
(trop ancien pour répondre)
Eric C.
2010-06-01 12:59:09 UTC
Permalink
Bonjour à tous (ça ne me rajeunit pas, la dernière fois que j'ai posté
dans ce groupe, d'après Google, c'était en avril 2001 ...), j'ai une
question plus "esthétique" qu'autre chose : comment construire le plus
simplement possible le hash "réciproque" d'une liste ? Si la liste
associe des valeurs aux indices, ce hash doit associer les indices aux
valeurs. Petit exemple pour illustrer :
@tab=("a","b","c","d");
$length=@tab;
for ($i=0;$i<$length;$i++) {
$my_hash{$tab[$i]}=$i;
}
J'imagine que ça doit pouvoir s'écrire en une ligne, mais je n'ai rien
trouvé ...


Eric
Paul Gaborit
2010-06-01 13:18:42 UTC
Permalink
À (at) Tue, 1 Jun 2010 05:59:09 -0700 (PDT),
Post by Eric C.
Bonjour à tous (ça ne me rajeunit pas, la dernière fois que j'ai posté
dans ce groupe, d'après Google, c'était en avril 2001 ...),
Bonjour,
Post by Eric C.
j'ai une
question plus "esthétique" qu'autre chose : comment construire le plus
simplement possible le hash "réciproque" d'une liste ? Si la liste
associe des valeurs aux indices, ce hash doit associer les indices aux
@tab=("a","b","c","d");
for ($i=0;$i<$length;$i++) {
$my_hash{$tab[$i]}=$i;
}
J'imagine que ça doit pouvoir s'écrire en une ligne, mais je n'ai rien
trouvé ...
En supposant que les valeurs sont uniques (bijection indice <-> valeur)
alors votre code est correct. On peut l'écrire de manière un tout petit
peu plus "perlienne" (mais pas obligatoirement plus efficace -- il
faudrait faire un benchmark) :

my @tab = "a" .. "z";
my %my_hash = map {($tab[$_] => $_)} 0..$#tab;
--
Paul Gaborit - <http://perso.mines-albi.fr/~gaborit/>
Perl en français - <http://perl.mines-albi.fr/>
Eric C.
2010-06-03 09:37:57 UTC
Permalink
Post by Paul Gaborit
En supposant que les valeurs sont uniques (bijection indice <-> valeur)
alors votre code est correct. On peut l' crire de mani re un tout petit
peu plus "perlienne" (mais pas obligatoirement plus efficace -- il
  my %my_hash = map {($tab[$_] => $_)} 0..$#tab;
Merci, je me doutais qu'il y avait une solution par map, mais mon perl
étant un peu rouillé ça n'était pas (re)venu.
Niveau lisibilité, difficile de dire quelle solution est la plus
compréhensible ...
Je n'avais effectivement pas pensé à une éventuelle non-bijectivité
(je n'ai pas ce problème, les valeurs sont des noms de colonnes
obligatoirement différents).

En tout cas je constate que les mêmes anges gardiens veillent encore
et toujours sur le ng :)

Eric
Jerome Quelin
2010-06-03 15:03:18 UTC
Permalink
Post by Paul Gaborit
Post by Eric C.
J'imagine que ça doit pouvoir s'écrire en une ligne, mais je n'ai rien
trouvé ...
En supposant que les valeurs sont uniques (bijection indice <-> valeur)
alors votre code est correct. On peut l'écrire de manière un tout petit
peu plus "perlienne" (mais pas obligatoirement plus efficace -- il
my %my_hash = map {($tab[$_] => $_)} 0..$#tab;
ou en utilisant des tranches de hash :
my %hash;
@hash{ @tab } = 0..$#tab;

jérôme
--
***@gmail.com
Paul Gaborit
2010-06-03 15:33:30 UTC
Permalink
À (at) Thu, 03 Jun 2010 17:03:18 +0200,
Post by Jerome Quelin
Post by Paul Gaborit
Post by Eric C.
J'imagine que ça doit pouvoir s'écrire en une ligne, mais je n'ai rien
trouvé ...
En supposant que les valeurs sont uniques (bijection indice <-> valeur)
alors votre code est correct. On peut l'écrire de manière un tout petit
peu plus "perlienne" (mais pas obligatoirement plus efficace -- il
my %my_hash = map {($tab[$_] => $_)} 0..$#tab;
my %hash;
@hash{ @tab } = 0..$#tab;
J'ai fait un petit benchmark pour voir et le résultat est sans appel :

Benchmark: timing 500000 iterations of foreach, map, slice...
foreach: 9.46806 wallclock secs ( 8.67 usr + 0.04 sys = 8.71 CPU)
@ 57405.28/s (n=500000)
map: 13.4544 wallclock secs (12.75 usr + 0.03 sys = 12.78 CPU)
@ 39123.63/s (n=500000)
slice: 6.38025 wallclock secs ( 6.05 usr + 0.02 sys = 6.07 CPU)
@ 82372.32/s (n=500000)

Il vaut donc mieux passer par les tranches de hash !

Pour ceux que ça intéresse, voici le code du benchmark :
############################
use Benchmark qw(:hireswallclock);

my @tab = "a" .. "z";

timethese(500000,
{
'map' => sub {
my %my_hash = map {($tab[$_] => $_)} 0..$#tab;
},
'foreach' => sub {
my %hash;
foreach (0..$#tab) {
$hash{$tab[$_]} = $_;
}
},
'slice' => sub {
my %hash;
@hash{ @tab } = 0..$#tab;
},
}
);
############################
--
Paul Gaborit - <http://perso.mines-albi.fr/~gaborit/>
Perl en français - <http://perl.mines-albi.fr/>
Loading...