Quelle est la façon la plus sûre d'itérer à travers les clés d'un hachage Perl?

Translate

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 utilisanteachpeut d'une manière ou d'une autre avoir des effets secondaires involontaires. Alors, est-ce vrai, et est-ce que l'une des deux méthodes suivantes est la meilleure ou y a-t-il une meilleure façon?

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

# Method 2
foreach my $key (keys(%hash)) {
    # Something
}
This question and all comments follow the "Attribution Required."

Toutes les réponses

Translate

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 jamaislisl'une des valeurs, utilisez les clés ():

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

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

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

Si vous avez besoin des clésetles 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 manière que ce soitsaufpour supprimer la clé courante 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 très bien 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, cependant, est 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
La source
Translate

Une chose dont vous devez être conscient lors de l'utilisationeachest qu'il a pour effet secondaire d'ajouter "état" à votre hachage (le hachage doit se souvenir de la clé "suivante"). Lorsque vous utilisez du code comme les extraits publiés ci-dessus, qui itèrent sur tout le hachage en une seule fois, ce n'est généralement pas un problème. Cependant, vous rencontrerez des problèmes difficiles à localiser (je parle d'expérience;), lors de l'utilisationeachavec des déclarations commelastoureturnpour sortir duwhile ... eachboucle avant d'avoir traité toutes les clés.

Dans ce cas, le hachage se souviendra des clés qu'il a déjà renvoyées et du moment où vous utilisezeachdessus la prochaine fois (peut-être dans un morceau de code totalement indépendant), 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";
}

Cela imprime:

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

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

La source
Translate

L'endroit oùeachpeut vous causer 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 avez besoin d'être sûr queeachobtient toutes les clés et valeurs, vous devez vous assurer que vous utilisezkeysouvaluesd'abord (car cela réinitialise l'itérateur). Voir ledocumentation pour chacun.

La source
Constance Lee
Translate

L'utilisation de chaque syntaxe empêchera la génération de l'ensemble de clés en même temps. Cela peut être important si vous utilisez un hachage lié à une base de données avec 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 alors que les clés génèrent en fait le tableau entier avant le début de la boucle.

Ainsi, le seul endroit où «chacun» est réellement utile est lorsque le hachage est très important (par rapport à la mémoire disponible). Cela ne se produira probablement que lorsque le hachage lui-même ne vit pas dans la mémoire elle-même, à 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 important et le plus facile à lire.

La source
Translate

Quelques réflexions diverses sur ce sujet:

  1. Il n'y a rien de dangereux à propos des 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 vous l'itérez. (Il est parfaitement sûr de modifier les valeurs.) Le seul effet secondaire potentiel auquel je puisse penser est quevaluesrenvoie des alias, ce qui signifie que leur modification modifiera le contenu du hachage. C'est par conception, mais peut ne pas être ce que vous voulez dans certaines circonstances.
  2. John'sréponse acceptéeest bon à 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é retournée pareach. C'estne pasvrai pourkeyscommeeachest un itérateur alors quekeysrenvoie une liste.
La source
Translate

J'utilise toujours aussi 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érencerez pas constamment le hachage.

La source
Translate

Je peux 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 chaque () est différent de keys () ou values () (autre que la réponse évidente "ils renvoient des choses différentes". En fait, la documentation indique que l'utilisation du même itérateur est utilisée. renvoie des valeurs de liste réelles au lieu de copies de celles-ci, et que modifier le hachage en l'itérant en utilisant 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 parfois 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, à quel point 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 =)

La source
Translate

J'utilise habituellementkeyset je ne peux pas penser à la dernière fois que j'ai utilisé ou lu une utilisation deeach.

N'oublie pasmap, en fonction de ce que vous faites dans la boucle!

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

Je dirais:

  1. Utilisez ce qui est le plus facile à lire / comprendre pour la plupart des gens (donc les clés, généralement, je dirais)
  2. Utilisez 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 re-factoriser 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.

La source