Praktyczną zasadą jest użycie funkcji najlepiej dostosowanej do Twoich potrzeb.
Jeśli chcesz tylko klucze i nie planujesz kiedykolwiek czytać żadnej z wartości, użyj keys ():
foreach my $key (keys %hash) { ... }
Jeśli chcesz tylko wartości, użyj wartości ():
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 jakikolwiek sposób, z wyjątkiem usunięcia bieżącego klucza podczas iteracji, nie możesz używać each (). Na przykład ten kod służący do tworzenia nowego zestawu kluczy z dużymi literami z podwojonymi wartościami działa dobrze przy użyciu keys ():
%h = (a => 1, b => 2);
foreach my $k (keys %h)
{
$h{uc $k} = $h{$k} * 2;
}
produkując oczekiwany wynikowy hash:
(a => 1, A => 2, b => 2, B => 4)
Ale używając each () do zrobienia tego samego:
%h = (a => 1, b => 2);
keys %h;
while(my($k, $v) = each %h)
{
$h{uc $k} = $h{$k} * 2; # BAD IDEA!
}
daje nieprawidłowe wyniki w trudny do przewidzenia sposób. Na przykład:
(a => 1, A => 2, b => 2, B => 8)
Jest to jednak bezpieczne:
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
Jedną rzeczą, o której powinieneś wiedzieć podczas używania
each
jest to, że ma to efekt uboczny dodawania „stanu” do twojego skrótu (hash musi pamiętać, jaki jest klucz „następny”). Podczas korzystania z kodu, takiego jak zamieszczone powyżej fragmenty, które powtarzają cały hash za jednym razem, zwykle nie stanowi to problemu. Jednak napotkasz trudne do wyśledzenia problemy (mówię z doświadczenia;), gdy używaszeach
razem z instrukcjami, takimi jaklast
lub,return
aby wyjść zwhile ... each
pętli, zanim przetworzysz wszystkie klucze.W takim przypadku hash zapamięta, które klucze już zwrócił, a gdy użyjesz
each
go następnym razem (być może w całkowicie niepowiązanym fragmencie kodu), będzie kontynuowany na tej pozycji.Przykład:
To drukuje:
Co się stało z klawiszami „bar” i baz ”? Nadal tam są, ale drugi
each
zaczyna się w miejscu, w którym skończył się pierwszy, i zatrzymuje się, gdy osiąga koniec hasha, więc nigdy nie widzimy ich w drugiej pętli.źródło
Miejscem, w którym
each
może powodować problemy, jest to, że jest to prawdziwy iterator bez zakresu. Na przykład:Jeśli chcesz mieć pewność, że
each
pobiera wszystkie klucze i wartości, musisz upewnić się, że używaszkeys
lub jakovalues
pierwszego (ponieważ resetuje to iterator). Zobacz dokumentację dla każdego .źródło
Użycie każdej składni zapobiegnie wygenerowaniu całego zestawu kluczy na raz. Może to być ważne, jeśli używasz powiązanego skrótu z bazą danych z milionami wierszy. Nie chcesz generować całej listy kluczy naraz i wyczerpać pamięć fizyczną. W tym przypadku każdy służy jako iterator, podczas gdy klucze faktycznie generują całą tablicę przed rozpoczęciem pętli.
Tak więc jedyne miejsce, w którym „każdy” jest rzeczywiste, to sytuacja, w której wartość skrótu jest bardzo duża (w porównaniu z dostępną pamięcią). Jest to prawdopodobne tylko wtedy, gdy sam hash nie żyje w pamięci, chyba że programujesz podręczne urządzenie do zbierania danych lub coś z małą pamięcią.
Jeśli pamięć nie jest problemem, zwykle paradygmat mapy lub kluczy jest bardziej rozpowszechnionym i łatwiejszym do odczytania paradygmatem.
źródło
Kilka różnych przemyśleń na ten temat:
values
przychodzi mi do głowy, jest zwracanie aliasów, co oznacza, że ich modyfikacja spowoduje zmianę zawartości skrótu. Jest to zgodne z projektem, ale w niektórych okolicznościach może nie być tym, czego chcesz.each
. Nie jest to prawdą w przypadku iteratorakeys
aseach
is iterator, podczas gdykeys
zwraca listę.źródło
Zawsze używam również metody 2. Jedyną korzyścią płynącą z używania każdego z nich jest to, że jeśli tylko odczytujesz (zamiast ponownie przypisywać) wartość wpisu skrótu, nie usuwasz stale odniesienia do skrótu.
źródło
Może mnie ugryzie, ale myślę, że to osobiste preferencje. Nie mogę znaleźć żadnego odniesienia w dokumentach do każdego () innego niż klucze () lub wartości () (poza oczywistą odpowiedzią „zwracają różne rzeczy”. W rzeczywistości dokumenty wskazują, że używają tego samego iteratora i wszystkie zwraca rzeczywiste wartości listy zamiast ich kopii, a modyfikowanie skrótu podczas iteracji po nim przy użyciu dowolnego wywołania jest złe.
Wszystko to powiedziawszy, prawie zawsze używam keys (), ponieważ dla mnie zwykle bardziej samodokumentuje się dostęp do wartości klucza za pośrednictwem samego skrótu. Czasami używam wartości (), gdy wartość jest odwołaniem do dużej struktury, a klucz do skrótu był już przechowywany w strukturze, w którym to momencie klucz jest zbędny i nie potrzebuję go. Myślę, że użyłem każdego () 2 razy w ciągu 10 lat programowania w Perlu i prawdopodobnie był to zły wybór za każdym razem =)
źródło
Zwykle używam
keys
i nie mogę sobie przypomnieć, kiedy ostatnio używałem lub czytałemeach
.Nie zapomnij o tym
map
, w zależności od tego, co robisz w pętli!źródło
Powiem:
Daje to 2 główne zalety:
Nie sądzę, aby używanie kluczy nad każdym z nich było droższe, więc nie ma potrzeby stosowania dwóch różnych konstrukcji dla tego samego elementu w kodzie.
źródło
keys
wzrostem użycia pamięci ohash-size * avg-key-size
. Biorąc pod uwagę, że rozmiar klucza jest ograniczona tylko przez pamięć (jak są one po prostu elementów tablicy, jak „ich” wartościami odpowiadającymi pod maską), w niektórych sytuacjach może być niewspółmiernie droższe zarówno zużycia pamięci i czasu potrzebnego do wykonania kopii.