2017-10-18 2 views
1

iterieren Mein Problem ist einfach. Ich muss einige Schlüssel-Wert-Paare aus einem Hash drucken. Ich habe versucht, zwei verschiedene Methoden:Verschiedene Ergebnisse über einen Hash in Perl

METHODE A

#Fill hash from file ($fh has been defined previously) 

while(my $line = <$fh>) { 
    $counter{$line}++; 
} 

foreach (my($k, $v) = each %counter){ 
    my ($p1, $p2) = split(/$;/o, $k); 

    chomp $p2; 

    if($v > 0){ 
     printf "%s_down_%s %s %d\n", $prefix, $p1, $p2, $v; 
     printf "%s_up_%s %s %d\n", $prefix, $p2, $p1, $v; 
    } 
} 

VERFAHREN B

#Fill hash from file ($fh has been defined previously) 

while(my $line = <$fh>) { 
    $counter{$line}++; 
} 

foreach $k (keys %counter) { 
    my ($p1, $p2) = split(/$;/o, $k); 
    my $v = $counter{$p1,$p2}; 

    chomp $p2; 

    if($v > 0){ 
     printf "%s_down_%s %s %d\n", $prefix, $p1, $p2, $v; 
     printf "%s_up_%s %s %d\n", $prefix, $p2, $p1, $v; 
    } 
} 

den Code lesen, ich würde sagen, beide Methoden das gleiche Ergebnis zu erhalten, aber, nach der Ausführung es, Methode B, bekam die richtigen Ergebnisse und Methode A gedruckt nur zwei Elemente (4 Zeilen).

Nachdem ich die Methode A mehrmals ausgeführt habe, kann ich sehen, dass die gedruckten Elemente jede Ausführung ändern.

Jeder weiß, was hier vor sich geht?

Antwort

2

Die Verwendung von foreach mit each ist falsch

perl -wE'%h=(a=>1,b=>2,c=>3); for (($k, $v) = each %h) { say "$k => $v" }' 

druckt

 
c => 3 
c => 3 

Mit while statt foreach wir das richtige Verhalten zu bekommen.

, dass dies falsch ist, kann aus each (Hervorhebung von mir)

Wenn auf einem Hash in Listenkontext aufgerufen zu sehen ist, gibt eine 2-Element-Liste aus dem Schlüssel und Wert für das nächste Element eines Hashes.

So each iteriert und seit foreach zuerst die ganze Liste erzeugt (iterieren) each wird intern in einer (deutlich) schlecht definierte Art und Weise zu durchlaufen; Dies kann ein undefiniertes Verhalten aufweisen. Wir erhalten eine falsche Liste von Schlüssel/Wert-Paaren für foreach, die übersprungen werden soll (intern gebildet unter Verwendung von each).

Lesen Sie each Seite sorgfältig für weitere Feinheiten bei der Verwendung von keys, values und each.


Ein anschauliches Beispiel

perl -wE'@h{("a".."f")} = (1..6); for (($k, $v) = each %h) { say "$k => $v" }' 

druckt auf meinem System

 
e => 5 
e => 5 

Wenn es richtig das e => 5 Paar mit while ausgeführt wird erste zurückgegeben. Also bekommen wir dieses Paar zweimal.

(Immer noch versuchen, warum zweimal, um herauszufinden, ...)

+0

@ IvánRodríguezTorres die Antwort aktualisiert, die beide durch das erste Beispiel der Bearbeitung und die Erklärung und das Hinzufügen von einem besseres Beispiel. Das Fazit ist natürlich das gleiche: 'for' ist falsch und man muss' while' verwenden. Aber um das Verhalten zu erklären, wenn 'for' verwendet wird, gibt es eine ganze Menge mehr. Danke für die Zuschreibung. – zdim

+1

Ihre Antwort ist sehr klar, wie immer. Vielen Dank. –

+0

@ IvánRodríguezTorres Froh, dass es geholfen hat und danke, dass Sie das gesagt haben. Es ist eine Freude, mit dir zu interagieren :) – zdim