Question

Si j'ai un hachage Perl avec un tas de paires (clé, valeur), quelle est la méthode préférée pour parcourir toutes les clés ?J'ai entendu dire qu'en utilisant each peut, d’une manière ou d’une autre, avoir des effets secondaires inattendus.Alors, est-ce vrai et l’une des deux méthodes suivantes est-elle la meilleure, ou existe-t-il une meilleure solution ?

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

# Method 2
foreach my $key (keys(%hash)) {
    # Something
}
Était-ce utile?

La solution

La règle générale est d’utiliser la fonction la plus adaptée à vos besoins.

Si vous voulez juste les clés et que vous n'envisagez jamais de le faire lire n'importe laquelle des valeurs, utilisez keys() :

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

Si vous voulez juste 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 envisagez de modifier les clés du hachage de quelque manière que ce soit sauf pour supprimer la clé actuelle pendant l’itération, vous ne devez pas utiliser each().Par exemple, ce code pour créer un nouvel ensemble de clés majuscules avec des valeurs doublées fonctionne correctement en utilisant keys() :

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

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

produire le hachage résultant attendu :

(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 est cependant sûr :

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

Tout cela est décrit dans la documentation Perl :

% perldoc -f keys
% perldoc -f each

Autres conseils

Une chose à laquelle vous devez être conscient lorsque vous utilisez each est qu'il a l'effet secondaire d'ajouter "state" à votre hachage (le hachage doit se souvenir quelle est la touche "suivante").Lorsque vous utilisez du code comme les snippets 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.Cependant, vous rencontrerez des problèmes difficiles à traquer (je parle de expérience ;), lors de l'utilisation each avec des déclarations commelast ou return sortir du while ... each boucle devant vous ont traité toutes les clés.

Dans ce cas, le hachage se souviendra des clés qu'il a déjà renvoyées, et lorsque vous utilisez each sur elle la prochaine fois (peut-être dans un morceau totalement sans 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";
}

Ceci imprime :

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

Qu'est-il arrivé aux touches "bar" et baz" ?Ils sont toujours là, mais le deuxième each commence là où le premier s'est arrêté et s'arrête lorsqu'il atteint la fin du hachage, donc nous ne les voyons jamais dans la deuxième boucle.

L'endroit où each peut vous poser des problèmes, c'est qu'il s'agit d'un véritable itérateur sans portée.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 devez être sûr que each obtient toutes les clés et valeurs, vous devez vous assurer que vous utilisez keys ou values d'abord (car cela réinitialise l'itérateur).Voir le documentation pour chacun.

L’utilisation de la syntaxe each empêchera la génération complète de l’ensemble des clés en même temps.Cela peut être important si vous utilisez un hachage lié à une base de données contenant des millions de lignes.Vous ne voulez pas générer la liste complète des clés d’un seul coup et épuiser votre mémoire physique.Dans ce cas, chacun sert d'itérateur alors que les clés génèrent en fait l'intégralité du tableau avant le début de la boucle.

Ainsi, le seul endroit où "chacun" est réellement utile est lorsque le hachage est très volumineux (par rapport à la mémoire disponible).Cela ne se produira probablement que lorsque le hachage lui-même ne réside pas dans la mémoire, à moins que vous ne programmiez un appareil de collecte de données portable ou quelque chose avec une petite mémoire.

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

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 est dangereux, c'est de modifier les clés d'un hachage pendant que vous le parcourez.(Il est parfaitement sûr de modifier les valeurs.) Le seul effet secondaire potentiel auquel je puisse penser est que values renvoie des alias, ce qui signifie que les modifier modifiera le contenu du hachage.C'est intentionnel, mais ce n'est peut-être pas ce que vous souhaitez dans certaines circonstances.
  2. Jean réponse acceptée c'est bien à une exception près :la documentation indique clairement qu'il n'est pas sûr d'ajouter des clés lors d'une itération 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 prudent de supprimer la dernière clé renvoyée par each.C'est pas vrai pour keys comme each est un itérateur tandis que keys renvoie une liste.

J'utilise toujours également la méthode 2.Le seul avantage de l'utilisation de chacun est que si vous lisez simplement (plutôt que de réattribuer) la valeur de l'entrée de hachage, vous ne déréférencez pas constamment le hachage.

Je pourrais me faire mordre par celui-ci mais je pense que c'est une préférence personnelle.Je ne trouve aucune référence dans la documentation indiquant que each() est différent de key() ou de values() (autre que la réponse évidente "ils renvoient des choses différentes".En fait, la documentation indique qu'il faut utiliser le même itérateur et qu'ils renvoient tous des valeurs de liste réelles au lieu de copies de celles-ci, et que modifier le hachage tout en itérant dessus à l'aide de n'importe quel appel est mauvais.

Cela dit, j'utilise presque toujours keys() car pour moi, il est généralement plus auto-documenté d'accéder à la valeur de la clé via le hachage lui-même.J'utilise occasionnellement values() lorsque la valeur est une référence à une grande structure et que la clé du hachage était déjà stockée dans la structure, auquel cas la clé est redondante et je n'en ai pas besoin.Je pense que j'ai utilisé each() 2 fois en 10 ans de programmation Perl et c'était probablement le mauvais choix les deux fois =)

J'utilise habituellement keys et je ne me souviens pas de la dernière fois que j'ai utilisé ou lu une utilisation de each.

N'oublie pas map, selon ce que vous faites dans la boucle !

map { print "$_ => $hash{$_}\n" } keys %hash;

Je dirais :

  1. Utilisez ce qui est le plus facile à lire/comprendre pour la plupart des gens (donc les clés, en général, je dirais)
  2. Utilisez tout ce que vous décidez de manière cohérente dans toute la base de code.

Cela donne 2 avantages majeurs :

  1. Il est plus facile de repérer le code « commun » afin que vous puissiez le refactoriser en fonctions/méthodes.
  2. C'est plus facile à maintenir pour les futurs développeurs.

Je ne pense pas qu'il soit plus coûteux d'utiliser des clés sur chacune, donc pas besoin de deux constructions différentes pour la même chose dans votre code.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top