Hva er den sikreste måten å iterere gjennom nøklene til en Perl hasj?

stemmer
87

Hvis jeg har en Perl hash med en haug av (nøkkel, verdi) for parene, hvilken er den foretrukne metode for å iterere gjennom alle tastene? Jeg har hørt at bruk eachkan i noen måte har utilsiktede bivirkninger. Så, er det sant, og er en av de to følgende metoder beste, eller er det en bedre måte?

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

# Method 2
foreach my $key (keys(%hash)) {
    # Something
}
Publisert på 06/08/2008 klokken 02:53
kilden bruker
På andre språk...                            


9 svar

stemmer
175

Tommelfingerregelen er å bruke funksjonen som passer best til dine behov.

Hvis du bare vil ha nøklene og ikke har tenkt å noensinne lese noen av verdiene, bruk tastene ():

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

Hvis du bare vil verdiene, bruker verdier ():

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

Hvis du trenger nøklene og verdiene, bruker hver ():

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

Hvis du planlegger å endre nøklene til hasj på noen måte , bortsett fra for å slette den aktuelle tasten under iterasjon, så du ikke må bruke hver (). For eksempel denne koden for å lage et nytt sett med store taster med doblet verdiene fungerer fint med tastene ():

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

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

fremstilling av det forventede resulterende hash:

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

Men ved hjelp av hver () for å gjøre det samme:

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

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

produserer feil resultater i vanskelige å forutsi måter. For eksempel:

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

Dette er imidlertid sikker:

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

Alt dette er beskrevet i perl dokumentasjon:

% perldoc -f keys
% perldoc -f each
Svarte 06/08/2008 kl. 13:22
kilden bruker

stemmer
21

En ting du bør være oppmerksom på når du bruker eacher at den har den bivirkning av å legge "stat" til din hash (hash må huske hva "neste" -tasten er). Ved bruk av kode som snutter postet ovenfor, som veksle over hele hash på en gang, er dette vanligvis ikke et problem. Men vil du støte på hardt for å spore problemer (Jeg snakker av erfaring;), ved bruk eachsammen med utsagn som lasteller returnå gå ut av while ... eachloopen før du har behandlet alle tastene.

I dette tilfellet vil hash huske hvilke taster som det allerede har kommet tilbake, og når du bruker eachpå det neste gang (kanskje i en totalt irrelevant stykke kode), vil det fortsette i denne stillingen.

Eksempel:

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

Dette skriver:

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

Hva skjedde med tastene "bar" og Baz "? De er fortsatt der, men den andre eachbegynner der den første slapp, og stopper når den når enden av hasj, slik at vi aldri se dem i andre sløyfen.

Svarte 15/09/2008 kl. 23:37
kilden bruker

stemmer
19

Stedet hvor eachkan føre til at du problemer er at det er en ekte, ikke-omfang iterator. Som et eksempel:

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

Hvis du trenger å være sikker på at eachfår alle nøkler og verdier, må du sørge for at du bruker keyseller valuesførste (som det resetter iterator). Se dokumentasjon for hver .

Svarte 16/09/2008 kl. 14:35
kilden bruker

stemmer
13

Bruke hver syntaks vil hindre hele settet med nøkler blir generert på en gang. Dette kan være viktig hvis du bruker en tie-ed hasj til en database med millioner av rader. Du ønsker ikke å generere hele listen av nøkler på en gang og eksos det fysiske minnet. I dette tilfellet har hver tjener som en iteratoren mens tastene faktisk genererer hele matrisen før sløyfen begynner.

Så, det eneste stedet "hver" er av reell bruk er når hash er meget stor (i forhold til ledig minne). Det er bare sannsynlig til å skje når hash selv ikke bor i minne seg selv med mindre du programmerer en håndholdt datainnsamlingsenhet eller noe med lite minne.

Hvis minnet er ikke et problem, vanligvis kartet eller nøkler paradigme er mer utbredt og lettere å lese paradigme.

Svarte 11/09/2008 kl. 22:04
kilden bruker

stemmer
5

Noen diverse tanker om dette emnet:

  1. Det er ikke noe usikkert om noen av hash iteratorer selv. Hva er usikre er å modifisere nøklene til en hash mens du gjentar over det. (Det er helt trygt å endre verdiene.) Den eneste mulige bivirkning jeg kan tenke på er at valuesreturnerer aliaser som betyr at du endrer dem vil endre innholdet i hasj. Dette er design, men kanskje ikke hva du vil i enkelte tilfeller.
  2. John aksepterte svaret er bra med ett unntak: dokumentasjonen er klart at det ikke er trygt å legge nøklene mens gjentar over en hash. Det kan fungere for noen datasett, men vil mislykkes for andre avhengig av hasj orden.
  3. Som allerede nevnt, er det trygt å slette den siste nøkkelen returneres av each. Dette er ikke sant for keyssom eacher en iterator mens keysreturnerer en liste.
Svarte 15/09/2008 kl. 21:36
kilden bruker

stemmer
4

Jeg bruker alltid metoden 2 også. Den eneste fordelen med å bruke hver er hvis du bare leser (i stedet for re-tildeling) verdien av hasj oppføring, du er ikke alltid de-referansene til hasj.

Svarte 06/08/2008 kl. 04:04
kilden bruker

stemmer
4

Jeg kan bli bitt av dette, men jeg tror at det er personlige preferanser. Jeg kan ikke finne noen referanse i docs til hver () er annerledes enn tastene () eller verdier () (andre enn den åpenbare "de returnerer forskjellige ting" svar. Faktisk docs oppgi bruk samme iterator og de alle returnere faktiske listen over verdier i stedet for kopier av dem, og at endring av hasj mens gjentar over det ved hjelp av en samtale er dårlig.

Alt som er sagt, jeg bruker nesten alltid nøkler () fordi for meg er det vanligvis mer selv dokumentere å få tilgang til nøkkel verdi via hasj selv. I og til bruker verdier () når verdien er en referanse til en stor struktur og nøkkelen til den hash ble allerede er lagret i strukturen, noe som medførte at nøkkelen er overflødig, og jeg ikke trenger det. Jeg tror jeg har brukt hver () 2 ganger i 10 år med Perl programmering og det var sannsynligvis feil valg begge ganger =)

Svarte 06/08/2008 kl. 03:43
kilden bruker

stemmer
1

Jeg pleier å bruke keys, og jeg kan ikke tenke på den siste gangen jeg brukte eller lese en bruk av each.

Ikke glem map, avhengig av hva du gjør i loop!

map { print "$_ => $hash{$_}\n" } keys %hash;
Svarte 22/08/2008 kl. 15:31
kilden bruker

stemmer
-2

Jeg flatskjermen ville si:

  1. Bruk hva er lettest å lese / forstå for folk flest (slik at tastene, som regel, jeg vil hevde)
  2. Bruk hva du bestemmer deg konsekvent trodde hele kodebasen.

Dette gir 2 store fordeler:

  1. Det er lettere å få øye på "vanlige" kode slik at du kan re-faktor i funksjoner / methiods.
  2. Det er lettere for fremtidige utviklere å vedlikeholde.

Jeg tror ikke det er dyrere å bruke tastene over hver, så ikke behov for to ulike konstruksjoner for det samme i koden.

Svarte 20/12/2010 kl. 12:46
kilden bruker

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