Frage

Was ist die bevorzugte Methode zum Durchlaufen aller Schlüssel, wenn ich einen Perl-Hash mit einer Reihe von (Schlüssel-Wert-)Paaren habe?Ich habe das gehört each kann in irgendeiner Weise unbeabsichtigte Nebenwirkungen haben.Stimmt das also und ist eine der beiden folgenden Methoden die beste, oder gibt es eine bessere Möglichkeit?

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

# Method 2
foreach my $key (keys(%hash)) {
    # Something
}
War es hilfreich?

Lösung

Als Faustregel gilt, dass Sie die Funktion verwenden, die Ihren Bedürfnissen am besten entspricht.

Wenn Sie nur die Schlüssel wollen und nicht vorhaben, es jemals zu tun lesen Um einen der Werte zu erhalten, verwenden Sie „keys()“:

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

Wenn Sie nur die Werte möchten, verwenden Sie „values()“:

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

Wenn Sie die Schlüssel brauchen Und die Werte, verwenden Sie every():

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

Wenn Sie vorhaben, die Schlüssel des Hashs in irgendeiner Weise zu ändern außer Um den aktuellen Schlüssel während der Iteration zu löschen, dürfen Sie nicht every() verwenden.Dieser Code zum Erstellen eines neuen Satzes von Großbuchstabenschlüsseln mit verdoppelten Werten funktioniert beispielsweise problemlos mit „keys()“:

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

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

Erzeugen des erwarteten resultierenden Hashs:

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

Aber verwenden Sie every(), um dasselbe zu tun:

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

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

führt auf schwer vorhersehbare Weise zu falschen Ergebnissen.Zum Beispiel:

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

Dies ist jedoch sicher:

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

All dies ist in der Perl-Dokumentation beschrieben:

% perldoc -f keys
% perldoc -f each

Andere Tipps

Eines sollten Sie bei der Verwendung beachten each ist, dass es den Nebeneffekt hat, Ihrem Hash "Status" hinzuzufügen (der Hash muss sich daran erinnern, was der "nächste" Schlüssel ist).Wenn Sie Code wie die oben geposteten Snippets verwenden, die auf einmal den gesamten Hash iterieren, ist dies normalerweise kein Problem.Sie werden jedoch schwierig auf die Probleme zukommen (ich spreche aus Erfahrung;), wenn Sie verwendet werden each zusammen mit Aussagen wielast oder return zum Verlassen des while ... each Schleifen Sie, bevor Sie alle Schlüssel verarbeitet haben.

In diesem Fall wird sich der Hash daran erinnern, welche Schlüssel es bereits zurückgegeben hat und wann Sie verwendet werden each Im nächsten Mal (vielleicht in einem nicht verwandten Code) wird es an dieser Position fortgesetzt.

Beispiel:

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";
}

Dies druckt:

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

Was ist mit den Tasten „bar“ und „baz“ passiert?Sie sind immer noch da, aber der zweite each beginnt dort, wo der erste aufgehört hat, und stoppt, wenn das Ende des Hashs erreicht ist, sodass wir sie in der zweiten Schleife nie sehen.

Der Ort, wo each Probleme bereiten kann, besteht darin, dass es sich um einen echten Iterator ohne Gültigkeitsbereich handelt.Als Beispiel:

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";
}

Wenn Sie sicher sein müssen, dass each Ruft alle Schlüssel und Werte ab, die Sie unbedingt verwenden müssen keys oder values zuerst (da dadurch der Iterator zurückgesetzt wird).Siehe die Dokumentation für jeden.

Durch die Verwendung der Each-Syntax wird verhindert, dass der gesamte Schlüsselsatz auf einmal generiert wird.Dies kann wichtig sein, wenn Sie einen verknüpften Hash mit einer Datenbank mit Millionen von Zeilen verwenden.Sie möchten nicht die gesamte Schlüsselliste auf einmal generieren und Ihren physischen Speicher erschöpfen.In diesem Fall dient jeder als Iterator, während „keys“ tatsächlich das gesamte Array generiert, bevor die Schleife beginnt.

Daher ist „jeder“ nur dann wirklich von Nutzen, wenn der Hash sehr groß ist (im Vergleich zum verfügbaren Speicher).Dies ist wahrscheinlich nur dann der Fall, wenn der Hash selbst nicht im Speicher selbst vorhanden ist, es sei denn, Sie programmieren ein tragbares Datenerfassungsgerät oder etwas mit kleinem Speicher.

Wenn das Gedächtnis kein Problem darstellt, ist normalerweise das Karten- oder Schlüsselparadigma das vorherrschende und leichter zu lesende Paradigma.

Ein paar verschiedene Gedanken zu diesem Thema:

  1. An den Hash-Iteratoren selbst ist nichts unsicher.Was unsicher ist, ist das Ändern der Schlüssel eines Hashs, während Sie ihn durchlaufen.(Es ist absolut sicher, die Werte zu ändern.) Der einzige mögliche Nebeneffekt, der mir einfällt, ist dieser values Gibt Aliase zurück, was bedeutet, dass durch deren Änderung der Inhalt des Hashs geändert wird.Dies ist beabsichtigt, entspricht aber unter bestimmten Umständen möglicherweise nicht Ihren Wünschen.
  2. Johns akzeptierte Antwort ist gut, mit einer Ausnahme:Aus der Dokumentation geht klar hervor, dass es nicht sicher ist, Schlüssel hinzuzufügen, während über einen Hash iteriert wird.Bei einigen Datensätzen funktioniert es möglicherweise, bei anderen schlägt es je nach Hash-Reihenfolge jedoch fehl.
  3. Wie bereits erwähnt, ist es sicher, den letzten von zurückgegebenen Schlüssel zu löschen each.Das ist nicht wahr für keys als each ist ein Iterator while keys gibt eine Liste zurück.

Ich verwende auch immer Methode 2.Der einzige Vorteil bei der Verwendung jedes einzelnen besteht darin, dass Sie den Hash nicht ständig dereferenzieren, wenn Sie nur den Wert des Hash-Eintrags lesen (anstatt ihn neu zuzuweisen).

Ich werde vielleicht davon gebissen, aber ich denke, dass es eine persönliche Vorliebe ist.Ich kann in den Dokumenten keinen Hinweis darauf finden, dass Each() sich von Keys() oder Values() unterscheidet (abgesehen von der offensichtlichen Antwort „Sie geben unterschiedliche Dinge zurück“).Tatsächlich wird in den Dokumenten angegeben, dass derselbe Iterator verwendet wird und alle tatsächliche Listenwerte anstelle von Kopien davon zurückgeben, und dass es schlecht ist, den Hash zu ändern, während mit einem beliebigen Aufruf darüber iteriert wird.

Abgesehen davon verwende ich fast immer „keys()“, da es für mich normalerweise selbstdokumentierender ist, über den Hash selbst auf den Wert des Schlüssels zuzugreifen.Gelegentlich verwende ich „values()“, wenn der Wert eine Referenz auf eine große Struktur ist und der Schlüssel für den Hash bereits in der Struktur gespeichert war. Zu diesem Zeitpunkt ist der Schlüssel überflüssig und ich benötige ihn nicht.Ich glaube, ich habe every() in 10 Jahren Perl-Programmierung 2 Mal verwendet und es war wahrscheinlich beide Male die falsche Wahl =)

Normalerweise benutze ich keys und ich kann mich nicht an das letzte Mal erinnern, als ich eine Verwendung von verwendet oder gelesen habe each.

Vergiss es nicht map, je nachdem, was Sie in der Schleife tun!

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

Ich würde sagen:

  1. Verwenden Sie das, was für die meisten Menschen am einfachsten zu lesen/zu verstehen ist (normalerweise also Schlüssel, würde ich argumentieren).
  2. Verwenden Sie alles, was Sie entscheiden, konsistent in der gesamten Codebasis.

Dies bietet zwei große Vorteile:

  1. Es ist einfacher, „allgemeinen“ Code zu erkennen, sodass Sie ihn in Funktionen/Methoden umwandeln können.
  2. Für zukünftige Entwickler ist die Wartung einfacher.

Ich glaube nicht, dass es teurer ist, Schlüssel einzeln zu verwenden, sodass Sie in Ihrem Code nicht zwei verschiedene Konstrukte für dasselbe verwenden müssen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top