Quelle est la meilleure façon de parcourir les clés d'un hachage Perl?

voix
84

Si j'ai un hachage Perl avec un tas de paires (clé, valeur), quelle est la méthode préférée itérer toutes les clés? J'ai entendu dire que l' utilisation de eachmai d' une certaine manière ont des effets secondaires non intentionnels. Alors, est - ce vrai, et est l' une des deux méthodes suivantes meilleurs, ou est - il une meilleure façon?

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

# Method 2
foreach my $key (keys(%hash)) {
    # Something
}
Créé 06/08/2008 à 03:53
source utilisateur
Dans d'autres langues...                            


9 réponses

voix
170

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 de ne jamais lire une des valeurs, utilisez les touches ():

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

Si vous voulez juste les valeurs, utilisez les valeurs ():

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

Si vous avez besoin des clés et des 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 changer les clés du hachage de quelque manière , sauf pour la suppression de la clé en cours lors de l'itération, alors vous ne devez pas utiliser each (). Par exemple, ce code pour créer un nouveau jeu de clés en majuscules avec des valeurs doublées fonctionne très bien à l' aide des touches ():

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

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

la production du hachage prévu:

(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 façon difficiles à prévoir. Par exemple:

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

Ceci, cependant, est sûre:

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
Créé 06/08/2008 à 14:22
source utilisateur

voix
21

Une chose que vous devez être conscient lors de l' utilisation eachest qu'il a l'effet secondaire d'ajouter « état » à votre hachage (le hachage doit se rappeler ce que la touche « suivant » est). Lorsque vous utilisez le code comme les extraits ci - dessus affichés, qui itérer sur le hachage tout en une seule fois, ce qui est généralement pas un problème. Cependant, vous courrez en dur pour traquer les problèmes (je parle d'expérience;), lors de l' utilisation eachavec des déclarations comme lastou returnpour sortir de la while ... eachboucle avant d'avoir traité toutes les clés.

Dans ce cas, le hachage se souviendra que des clés est déjà revenu, et lorsque vous utilisez eachsur la prochaine fois (peut - être dans une pièce sans rapport avec totaly de 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 impression:

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

Ce qui est arrivé aux clés « bar » et baz "? Ils sont toujours là, mais le second eachcommence là où le premier avait laissée, et arrête quand il atteint la fin du hachage, de sorte que nous ne les voyons jamais dans la seconde boucle.

Créé 16/09/2008 à 00:37
source utilisateur

voix
19

L'endroit où eachpeut vous causer des problèmes est qu'il est un vrai, iterator portée non limité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 eachreçoit toutes les clés et les valeurs, vous devez vous assurer que vous utilisez keysou valuespremier (comme qui remet à zéro l'itérateur). Consultez la documentation de chaque .

Créé 16/09/2008 à 15:35
source utilisateur

voix
13

En utilisant la syntaxe de chaque empêchera tout l'ensemble des clés d'être générées à la fois. Cela peut être important si vous utilisez un hachage tie-ed à une base de données avec des millions de lignes. Vous ne voulez pas générer la liste complète des clés à la fois et épuiser votre mémoire physique. Dans ce cas, chaque sert d'iterator alors que les clés génère en fait l'ensemble du réseau avant que la boucle commence.

Ainsi, le seul endroit « chacun » est d'utilisation réelle est lorsque le hachage est très grand (par rapport à la mémoire disponible). C'est susceptible de se produire lorsque le hachage lui-même ne vit pas dans la mémoire elle-même à moins que vous la programmation d'un dispositif de collecte de données portable ou quelque chose avec une petite mémoire.

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

Créé 11/09/2008 à 23:04
source utilisateur

voix
5

Quelques réflexions diverses sur ce sujet:

  1. Il n'y a rien à risque sur l' un des itérateurs de hachage eux - mêmes. Ce qui est dangereux est de modifier les clés d'un hachage pendant que nous parcourons dessus. (Il est parfaitement sûr de modifier les valeurs.) Le seul potentiel effet secondaire que je peux penser est que valuesrenvoie des alias qui signifie que les modifier modifiera le contenu du hachage. Ceci est par la conception , mais peut - être pas ce que vous voulez dans certaines circonstances.
  2. John réponse acceptée est bonne à une exception près: la documentation est clair que ce n'est pas sûr d'ajouter des clés en itérer sur un hachage. Il peut fonctionner pour certains ensembles de données , mais échouera pour d' autres en fonction de l'ordre de hachage.
  3. Comme indiqué plus haut, il est sûr de supprimer la dernière clé retournée par each. Ceci est pas vrai pour keysque eachest un itérateur tout keysretourne une liste.
Créé 15/09/2008 à 22:36
source utilisateur

voix
4

J'utilise toujours la méthode 2 aussi bien. Le seul avantage d'utiliser chacun est si vous lisez juste (plutôt que réattribuant) la valeur de l'entrée de hachage, vous n'êtes pas constamment de-référencement le hachage.

Créé 06/08/2008 à 05:04
source utilisateur

voix
4

Je mordu par celui-ci mais je pense qu'il est de préférence personnelle. Je ne peux trouver aucune référence dans les documents à chaque () étant différente de touches () ou des valeurs () (autres que l'évidence « ils renvoient des choses différentes » réponse. En fait, les documents indiquent l'utilisation de la même iterator et ils ont tous retour des valeurs de la liste réelle au lieu de copies, et que la modification du hachage en itérer sur l'aide de tout appel est mauvais.

Tout cela étant dit, je presque toujours utiliser les touches () parce que pour moi il est généralement plus auto-documenté pour accéder à la valeur de la clé via le hachage lui-même. J'utilise parfois des valeurs () lorsque la valeur est une référence à une grande structure et la clé de hachage a déjà été stocké dans la structure, à quel point la clé est redondant et je ne suis pas besoin. Je pense que je l'ai utilisé each () 2 fois en 10 ans de programmation Perl et il était sans doute le mauvais choix deux fois =)

Créé 06/08/2008 à 04:43
source utilisateur

voix
1

Je l'habitude d' utiliser keyset je ne peux pas penser à la dernière fois que je ou lu une utilisation de each.

Ne pas oublier map, en fonction de ce que vous faites dans la boucle!

map { print "$_ => $hash{$_}\n" } keys %hash;
Créé 22/08/2008 à 16:31
source utilisateur

voix
-2

Je conseillerai dis:

  1. Utilisez tout ce qui est plus facile à lire / comprendre pour la plupart des gens (donc les clés, le plus souvent, je dirais)
  2. Utilisez ce que vous décidez toujours throught la base de code entier.

Cela donne 2 avantages majeurs:

  1. Il est plus facile de repérer le code « commun » de sorte que vous pouvez re-facteur dans les fonctions / methiods.
  2. Il est plus facile pour les développeurs futurs à maintenir.

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

Créé 20/12/2010 à 13:46
source utilisateur

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more