Jaki jest najbezpieczniejszy sposób iterację kluczy mieszania Perl?

głosy
84

Jeśli mam mieszania Perl z grupą (klucz, wartość) pary, co jest preferowaną metodą iteracji przez wszystkie klucze? Słyszałem, że przy użyciu eachmaja w jakiś sposób mieć niezamierzone skutki uboczne. Tak, to prawda, i jest jednym z dwóch następujących metod najlepszych, czy jest jakiś lepszy sposób?

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

# Method 2
foreach my $key (keys(%hash)) {
    # Something
}
Utwórz 06/08/2008 o 03:53
źródło użytkownik
W innych językach...                            


9 odpowiedzi

głosy
170

Zasadą jest, aby korzystać z funkcji najbardziej dopasowanego do Twoich potrzeb.

Jeśli chcesz tylko klucze i nie planuje kiedykolwiek przeczytać żadnej z wartości za pomocą przycisków ():

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

Jeśli chcesz tylko wartości, należy użyć wartości ():

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

Jeśli potrzebujesz klucze 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 mieszania w jakikolwiek sposób z wyjątkiem usuwania bieżącego klucza podczas iteracji, to nie musi korzystać each (). 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;
}

wytwarzania oczekiwanej otrzymanego mieszania:

(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!
}

produkuje nieprawidłowych wyników w trudnych do przewidzenia sposób. Na przykład:

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

To jednak jest 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
Odpowiedział 06/08/2008 o 14:22
źródło użytkownik

głosy
21

Jedną rzeczą, którą powinieneś być świadomy podczas korzystania eachjest to, że efekt uboczny „państwo”, dodając do swojej hash (hash musi pamiętać, co „next” klucz). Kiedy przy użyciu kodu podobnego do fragmentów zamieszczonych powyżej, które iteracyjne nad całym hash za jednym razem, to zazwyczaj nie jest problemem. Jednak będzie napotkasz trudne do wyśledzenia problemów (mówię z doświadczenia), przy użyciu eachwraz z oświadczeniami, jak lasti returndo wyjścia z while ... eachpętli przed przetworzeniu wszystkich kluczy.

W tym przypadku, hash będzie pamiętał, które klawisze zostały już zwrócone, a kiedy użyć eachna nim następnym razem (być może w totalnie niepowiązanych kawałek kodu), że 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 eachzaczyna się tam gdzie pierwsza została przerwana, i zatrzymuje się, gdy osiągnie koniec mieszania, więc nigdy nie zobaczyć je w drugiej pętli.

Odpowiedział 16/09/2008 o 00:37
źródło użytkownik

głosy
19

W miejscu, gdzie eachmoże spowodować problemy jest to, że jest to prawda, nie lunetą 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 eachdostaje wszystkie klucze i wartości, trzeba koniecznie używać keyslub valuespierwszy (jako że resetuje iterator). Zobacz dokumentację dla każdego .

Odpowiedział 16/09/2008 o 15:35
źródło użytkownik

głosy
13

Korzystanie z każdą składnię uniemożliwi cały komplet kluczy od generowane naraz. To może być ważne, jeśli używasz skrótu tie-ed do bazy danych z milionami wierszy. Nie chcesz, aby wygenerować całą listę klawiszy naraz i wyczerpuje swoją pamięć fizyczną. W tym przypadku każdy z nich służy jako iteracyjnej natomiast klucze faktycznie wytwarza całą tablicę przed rozpoczęciem pętli.

Więc jedynym miejscem „każdy” jest rzeczywistym użyciu jest gdy hash jest bardzo duża (w porównaniu do ilości dostępnej pamięci). To jest prawdopodobnie tylko się zdarzyć, kiedy sam hash nie mieszka w pamięci Sam chyba jesteś programowania ręcznego urządzenie do pobierania danych lub coś z małej pamięci.

Jeśli pamięć nie jest problemem, zwykle klucze map lub paradygmat jest bardziej prevelant i łatwiejsze do odczytania paradygmatu.

Odpowiedział 11/09/2008 o 23:04
źródło użytkownik

głosy
5

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

  1. Nie ma nic niebezpieczne o którymkolwiek z samych iteratorów hash. Co to jest niebezpieczne modyfikując klucze mieszania gdy jesteś iteracji nad nim. (Jest to całkowicie bezpieczne, aby zmodyfikować wartości.) Jedynym potencjalnym efektem ubocznym mogę pomyśleć, to że valueswraca aliasy co oznacza, że ich modyfikacji będą modyfikować zawartość hash. Jest to zgodne z projektem, ale nie może być to, co chcesz w pewnych okolicznościach.
  2. Jana akceptowane odpowiedź jest dobra, z jednym wyjątkiem: dokumentacja jest jasne, że to nie jest bezpieczne, aby dodać klucze podczas iteracji nad hash. Może on pracować dla niektórych zestawów danych, ale nie dla innych, w zależności od kolejności mieszania.
  3. Jak już wspomniano, jest to bezpieczne, aby usunąć ostatnią klucz zwrócony przez each. To nie prawda, za keysjak eachto iterator podczas keysZwraca listę.
Odpowiedział 15/09/2008 o 22:36
źródło użytkownik

głosy
4

Zawsze używam metody 2, jak również. Jedyną zaletą korzystania każdy jest, jeśli jesteś po prostu czytanie (zamiast ponownym przypisaniem) wartość wpisu hash, nie jesteś stale de-przedstawieniu hash.

Odpowiedział 06/08/2008 o 05:04
źródło użytkownik

głosy
4

Mogę dostać ukąszony przez ten jeden, ale myślę, że to osobiste preferencje. Nie mogę znaleźć żadnego odniesienia w Dokumentach each () jest inny niż klucze () lub () (wartości innych niż oczywisty „wracają różne rzeczy” odpowiedź. W rzeczywistości docs określać użycie tego samego iterator i wszyscy powrót wartości rzeczywiste list zamiast kopii nich, że modyfikując hash podczas iteracji nad nim za pomocą dowolnego połączenia jest zła.

Wszystko, co powiedział, to prawie zawsze używać klawiszy (), ponieważ dla mnie to jest zwykle bardziej samodzielny dokumentowanie dostęp do wartości kluczem za pośrednictwem samego skrótu. I od czasu do czasu użyć wartości (), gdy wartość jest odwołaniem do dużej struktury i klucz do mieszania został już zapisany w strukturze, w którym momencie kluczem jest zbędne i nie potrzebne. Myślę Użyłem each () 2 razy w ciągu 10 lat programowania Perl i to chyba zły wybór zarówno razy =)

Odpowiedział 06/08/2008 o 04:43
źródło użytkownik

głosy
1

Zwykle używam keysi nie mogę myśleć o ostatni raz użyłem lub poczytać wykorzystanie each.

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

map { print "$_ => $hash{$_}\n" } keys %hash;
Odpowiedział 22/08/2008 o 16:31
źródło użytkownik

głosy
-2

woudl mówię:

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

W ten sposób 2 główne zalety:

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

Nie sądzę, że jest to bardziej kosztowne w użyciu klawiszy na siebie, więc nie ma potrzeby dwóch różnych konstruktów do tej samej rzeczy w kodzie.

Odpowiedział 20/12/2010 o 13:46
źródło użytkownik

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