2008-08-06 12 views
80

Jeśli mam hash Perla z parą (klucz, wartość) par, jaka jest preferowana metoda iteracji przez wszystkie klucze? Słyszałem, że używanie each może w pewien sposób mieć niezamierzone efekty uboczne. Czy to prawda, i jest jedną z dwóch poniższych metod najlepiej, czy jest lepszy sposób?Jaki jest najbezpieczniejszy sposób na iterację za pomocą kluczy skrótu Perla?

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

# Method 2 
foreach my $key (keys(%hash)) { 
    # Something 
} 

Odpowiedz

161

Zasadą jest użycie funkcji najlepiej dopasowanej do potrzeb.

Jeśli chcesz tylko klucze i nie planuje kiedykolwiek czytać dowolnej wartości za pomocą przycisków():

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

Jeśli chcesz tylko wartości, wartości mechanicznych():

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

Jeśli potrzebujesz kluczy i wartości, użyj each():

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

Jeśli planujesz zmienić klucze skrótu w dowolny sposób z wyjątkiem dla usunięcia bieżącego klucza podczas iteracji, to nie możesz używać każdego(). Na przykład, ten kod, aby utworzyć nowy zestaw kluczy wielkimi podwójnymi wartości działa prawidłowo za pomocą klawiszy():

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

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

produkującą oczekiwany wynikający hash:

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

Ale używając each(), aby zrobić to samo:

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

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

daje niepoprawne wyniki w trudnych do przewidzenia sposoby.Na przykład:

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

To jednak jest bezpieczny:

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

Wszystko to jest opisane w dokumentacji Perla:

% perldoc -f keys 
% perldoc -f each 
+6

Proszę dodać klucze kontekstu pustki% h; przed każdą pętlą, aby bezpiecznie pokazać za pomocą iteratora. – ysth

+5

Jest inne zastrzeżenie z każdym. Iterator jest związany z hashem, a nie z kontekstem, co oznacza, że ​​nie jest on ponownie wprowadzany. Na przykład, jeśli zapętlisz się po hashu i wydrukujesz hash, perl wewnętrznie zresetuje iterator, tworząc pętlę bez końca: my% hash = (a => 1, b => 2, c => 3,); while (my ($ k, $ v) = każdy% hash) { wydrukuj% hash; } Przeczytaj więcej na http://blogs.perl.org/users/rurban/2014/04/do-not-use-each.html – Rawler

3

Mogę zostać ukąszony przez tego, ale myślę, że to osobiste preferencje. Nie mogę znaleźć żadnego odniesienia w dokumentacji dla każdego() jest inny niż keys() lub wartości() (inne niż oczywiste odpowiedzi "zwracają różne rzeczy." W rzeczywistości dokumenty mówią o użyciu tego samego iteratora i wszystkie one zwracają rzeczywiste wartości list zamiast ich kopii, a modyfikowanie hasza podczas iteracji nad nim za pomocą dowolnego połączenia jest złe.

Wszystko to powiedziawszy, prawie zawsze używam klawiszy(), ponieważ dla mnie zwykle jest to bardziej samodzielne dokumentowanie uzyskać dostęp do wartości klucza za pośrednictwem samego skrótu.Zamówię czasami używać wartości(), gdy wartość jest odniesieniem do dużej struktury, a klucz do skrótu był już przechowywany w strukturze, w którym to momencie klucz jest zbędny, a ja nie Potrzebuję tego. Myślę, że użyłem każdego() 2 razy w ciągu 10 lat programowania Perla i prawdopodobnie był to zły wybór za każdym razem =)

4

Zawsze używam również metody 2. Jedyna korzyść z używania każdego z nich polega na tym, że po prostu czytasz (zamiast ponownie przypisujesz) wartość hasła hash, nie odsyłasz ciągle hasha.

1

zwykle wykorzystać keys i nie mogę myślę, że ostatnio użyłem lub przeczytałem użycie each.

Nie zapomnij o map, w zależności od tego, co robisz w pętli!

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

nie używaj mapy, chyba że chcesz uzyskać wartość zwrotu –

12

Użycie każdej składni uniemożliwi jednoczesne wygenerowanie całego zestawu kluczy. Może to być ważne, jeśli używasz powiązanego skrótu do bazy danych zawierającej miliony wierszy. Nie chcesz generować całej listy kluczy naraz i wyczerpujesz swoją pamięć fizyczną. W tym przypadku każdy służy jako iterator, podczas gdy klucze faktycznie generują całą tablicę przed rozpoczęciem pętli.

Jedynym miejscem, w którym "każdy" jest w rzeczywistości, jest sytuacja, w której skrót jest bardzo duży (w porównaniu do dostępnej pamięci). To może się zdarzyć tylko wtedy, gdy samo hash nie będzie żyło w pamięci, chyba że programujesz urządzenie do zbierania danych lub coś z małą pamięcią.

Jeśli pamięć nie stanowi problemu, zwykle paradygmat mapy lub kluczy jest bardziej zaawansowanym i łatwiejszym do odczytania paradygmatem.

4

Kilka różne myśli na ten temat:

  1. Nie ma nic niebezpieczne o jakichkolwiek mieszania iteratory siebie. Niebezpieczne jest modyfikowanie kluczy skrótu podczas iteracji. (Całkowicie bezpieczne jest modyfikowanie wartości.) Jedynym potencjalnym efektem ubocznym, jaki mogę sobie wyobrazić, jest to, że values zwraca aliasy, co oznacza, że ​​modyfikowanie ich spowoduje modyfikację zawartości skrótu. Jest to zgodne z projektem, ale może nie być tym, co chcesz w pewnych okolicznościach.
  2. John's accepted answer jest dobry z jednym wyjątkiem: dokumentacja jest jasna, że ​​nie jest bezpiecznie dodawać klucze podczas iteracji przez skrót. Może działać dla niektórych zestawów danych, ale zawiedzie dla innych w zależności od kolejności hasza.
  3. Jak już wspomniano, można bezpiecznie usunąć ostatni klucz zwrócony przez each. Jest to , a nie true dla keys jako iterator, podczas gdy keys zwraca listę.
+2

Re "not" true dla kluczy ", raczej: nie dotyczy kluczy, a każde usunięcie jest bezpieczne. Frazowanie, którego używasz, oznacza, że ​​nigdy nie jest bezpiecznie usuwać niczego podczas używania kluczy. – ysth

+2

Odp: "nic niebezpiecznego w żadnym z iteratorów kryptograficznych", drugim niebezpieczeństwem jest założenie, że iterator jest na początku przed rozpoczęciem każdej pętli, jak wspominają inni. – ysth

20

Jedno powinien być świadomy podczas korzystania each jest to, że ma efekt uboczny dodając „państwo” do hash (hash musi pamiętać co „next” klucz). Podczas korzystania z kodu podobnego do opisanego powyżej fragmentu kodu, , który za każdym razem przechodzi przez cały skrót, zwykle nie jest to problem związany z . Jednak będzie napotkasz trudne do wyśledzenia problemów (mówię z doświadczenia ), przy użyciu each wraz ze sprawozdaniem jak last lub return aby wyjść z pętli while ... each przed przetworzeniu wszystkich kluczy.

W tym przypadku, hash będzie pamiętał, które klawisze zostały już zwrócone, a podczas korzystania each na nim następnym razem (być może w totalnie niepowiązanych kawałek kodu ), będzie nadal na tym stanowisku.

Przykład:

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

Drukuje:

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

Co stało się z kluczami "bar" i Baz "Są tam jeszcze, ale drugi each gdzie rozpoczyna się pierwszy? Zostało wyłączone i zatrzymuje się, gdy dojdzie do końca skrótu, więc nigdy nie widzimy ich w drugiej pętli

18

Miejsce, gdzie each może powodować problemy jest to, że jest to prawdziwy, nieskrócony iterator. Tytułem przykładu:

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

Jeśli chcesz mieć pewność, że each dostaje wszystkie klucze i wartości, trzeba koniecznie używać keys lub values pierwszy (jako że resetuje iterator). Zobacz documentation for each.

+0

To może odgryźć w pliku $$, jeśli nie, ostrożnie. – sdkks

-2

woudl mówię:

  1. Użyj wszelkimi najłatwiej odczytać/zrozumienia dla większości ludzi (tak, klucze, zwykle, będę argumentować)
  2. Zastosowanie cokolwiek zdecydować konsekwentnie throught całego kodu bazowego.

To daje 2 główne zalety:

  1. Łatwiej spot "wspólny" kod, dzięki czemu można ponownie czynnik do funkcji/methiods.
  2. Utrzymanie jest łatwiejsze dla przyszłych programistów.

Nie wydaje mi się, że używanie kluczy nad każdym z nich jest droższe, więc nie ma potrzeby stosowania dwóch różnych konstrukcji dla tego samego kodu.

+0

Z użyciem 'kluczy' użycie pamięci wzrasta o' rozmiar skrótu * średnica-klucza ". –

Powiązane problemy