102 votes

Quel est le moyen le plus sûr d'itérer parmi les clés d'un hachage Perl ?

Si j'ai un hachage Perl avec un tas de paires (clé, valeur), quelle est la méthode préférée pour itérer à travers toutes les clés ? J'ai entendu dire que l'utilisation de each peuvent d'une certaine manière avoir des effets secondaires involontaires. Alors, est-ce vrai, et l'une des deux méthodes suivantes est-elle la meilleure, ou existe-t-il un meilleur moyen ?

# Method 1
while (my ($key, $value) = each(%hash)) {
    # Something
}

# Method 2
foreach my $key (keys(%hash)) {
    # Something
}

193voto

John Siracusa Points 8106

La règle de base est d'utiliser la fonction la plus adaptée à vos besoins.

Si vous voulez juste les clés et ne prévoyez pas de jamais lire l'une des valeurs, utilisez keys() :

foreach my $key (keys %hash) { ... }

Si vous voulez seulement les valeurs, utilisez values() :

foreach my $val (values %hash) { ... }

Si vous avez besoin des clés et les valeurs, utilisez each() :

keys %hash; # reset the internal iterator so a prior each() doesn't affect the loop
while(my($k, $v) = each %hash) { ... }

Si vous prévoyez de modifier les clés du hachage de quelque façon que ce soit sauf pour effacer la clé courante pendant l'itération, alors vous ne devez pas utiliser each(). Par exemple, ce code pour créer un nouvel ensemble de clés en majuscules avec des valeurs doublées fonctionne bien en utilisant keys() :

%h = (a => 1, b => 2);

foreach my $k (keys %h)
{
  $h{uc $k} = $h{$k} * 2;
}

produisant le résultat attendu du hachage :

(a => 1, A => 2, b => 2, B => 4)

Mais en utilisant each() pour faire la même chose :

%h = (a => 1, b => 2);

keys %h;
while(my($k, $v) = each %h)
{
  $h{uc $k} = $h{$k} * 2; # BAD IDEA!
}

produit des résultats incorrects de manière difficile à prévoir. Par exemple :

(a => 1, A => 2, b => 2, B => 8)

Ceci, cependant, est sûr :

keys %h;
while(my($k, $v) = each %h)
{
  if(...)
  {
    delete $h{$k}; # This is safe
  }
}

Tout ceci est décrit dans la documentation de perl :

% perldoc -f keys
% perldoc -f each

25voto

8jean Points 2506

Une chose dont vous devez être conscient lorsque vous utilisez each est qu'il a l'effet secondaire d'ajouter de l'"état" à votre hachage (le hachage doit se rappeler de la clé "suivante"). Lorsque vous utilisez du code comme les extraits postés ci-dessus, qui itèrent sur l'ensemble du hachage en une seule fois, ce n'est généralement pas un problème. problème. Cependant, vous rencontrerez des problèmes difficiles à localiser (je parle par expérience ;), lorsque vous utilisez each ainsi que des déclarations telles que last ou return pour sortir de la while ... each boucle avant d'avoir avant d'avoir traité toutes les clés.

Dans ce cas, le hachage se souviendra des clés qu'il a déjà retournées, et lorsque vous utilisez each sur elle la prochaine fois (peut-être dans un morceau de code sans aucun rapport code), il continuera à cette position.

Exemple :

my %hash = ( foo => 1, bar => 2, baz => 3, quux => 4 );

# find key 'baz'
while ( my ($k, $v) = each %hash ) {
    print "found key $k\n";
    last if $k eq 'baz'; # found it!
}

# later ...

print "the hash contains:\n";

# iterate over all keys:
while ( my ($k, $v) = each %hash ) {
    print "$k => $v\n";
}

Cette empreinte :

found key bar
found key baz
the hash contains:
quux => 4
foo => 1

Qu'est-il arrivé aux clés "bar" et "baz" ? Elles sont toujours là, mais la deuxième each commence là où la première s'est arrêtée, et s'arrête lorsqu'elle atteint la fin du hachage, de sorte que nous ne les voyons jamais dans la deuxième boucle.

20voto

Darren Meyer Points 1083

L'endroit où each peut vous poser des problèmes, c'est qu'il s'agit d'un véritable itérateur non scindé. A titre d'exemple :

while ( my ($key,$val) = each %a_hash ) {
    print "$key => $val\n";
    last if $val; #exits loop when $val is true
}

# but "each" hasn't reset!!
while ( my ($key,$val) = each %a_hash ) {
    # continues where the last loop left off
    print "$key => $val\n";
}

Si vous avez besoin d'être sûr que each obtient toutes les clés et les valeurs, vous devez vous assurer que vous utilisez keys ou values d'abord (car cela réinitialise l'itérateur). Voir le la documentation pour chaque .

13voto

L'utilisation de la syntaxe each permet d'éviter que l'ensemble des clés soit généré en une seule fois. Cela peut être important si vous utilisez un hachage lié à une base de données comportant des millions de lignes. Vous ne voulez pas générer la liste complète des clés en une seule fois et épuiser votre mémoire physique. Dans ce cas, chacun sert d'itérateur, tandis que keys génère en fait le tableau entier avant le début de la boucle.

Ainsi, le seul endroit où "chaque" est vraiment utile est lorsque le hachage est très grand (par rapport à la mémoire disponible). Cela n'est susceptible de se produire que lorsque le hachage lui-même ne vit pas dans la mémoire, à moins que vous ne programmiez un dispositif de collecte de données portatif ou quelque chose avec une petite mémoire.

Si la mémoire n'est pas un problème, le paradigme des cartes ou des clés est généralement le plus répandu et le plus facile à lire.

6voto

Michael Carman Points 21983

Quelques réflexions diverses sur ce sujet :

  1. Il n'y a rien de dangereux dans les itérateurs de hachage eux-mêmes. Ce qui n'est pas sûr, c'est de modifier les clés d'un hachage pendant que l'on itère sur celui-ci. (La modification des valeurs est parfaitement sûre.) Le seul effet secondaire potentiel auquel je pense est que values renvoie des alias, ce qui signifie que leur modification entraînera une modification du contenu du hachage. Cela est prévu mais peut ne pas être ce que vous voulez dans certaines circonstances.
  2. John's réponse acceptée est bonne à une exception près : la documentation indique clairement qu'il n'est pas sûr d'ajouter des clés tout en itérant sur un hachage. Cela peut fonctionner pour certains ensembles de données mais échouera pour d'autres en fonction de l'ordre de hachage.
  3. Comme déjà noté, il est sûr de supprimer la dernière clé retournée par each . C'est pas vrai pour keys comme each est un itérateur tandis que keys renvoie une liste.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X