Powiedzmy, że mam tablicę i wiem, że będę robił dużo pytań „Czy tablica zawiera X?” czeki. Efektywnym sposobem na to jest przekształcenie tej tablicy w skrót, w którym klucze są elementami tablicy, a następnie możesz po prostu powiedzieć
if ($ hash {X}) {...}
Czy istnieje łatwy sposób na wykonanie tej konwersji tablicy na skrót? W idealnym przypadku powinien być wystarczająco wszechstronny, aby wziąć anonimową tablicę i zwrócić anonimowy skrót.
@hash{@keys} = undef;
Składnia tutaj, w której odnosisz się do skrótu za
@
pomocą skrótu, to wycinek z krzyżykiem. Zasadniczo mówimy$hash{$keys[0]}
AND$hash{$keys[1]}
AND$hash{$keys[2]}
... to lista po lewej stronie =, wartość l, i przypisujemy ją do tej listy, która w rzeczywistości trafia do skrótu i ustawia wartości dla wszystkich nazwanych kluczy. W tym przypadku$hash{$keys[0]}
podałem tylko jedną wartość, więc wartość trafia do , a pozostałe wpisy hash wszystkie automatycznie ożywają (ożywają) z niezdefiniowanymi wartościami. [Moją pierwotną sugestią było ustawienie wyrażenia = 1, które ustawiłoby ten jeden klawisz na 1, a pozostałe naundef
. Zmieniłem to dla spójności, ale jak zobaczymy poniżej, dokładne wartości nie mają znaczenia.]Kiedy zdasz sobie sprawę, że lwartość, wyrażenie po lewej stronie =, jest listą zbudowaną z hasha, wtedy zacznie mieć sens, dlaczego tego używamy
@
. [Chyba że to się zmieni w Perlu 6.]Chodzi o to, że używasz skrótu jako zestawu. Nie liczy się wartość, którą przypisuję; to tylko istnienie kluczy. Więc to, co chcesz zrobić, to nie coś takiego:
if ($hash{$key} == 1) # then key is in the hash
zamiast:
if (exists $hash{$key}) # then key is in the set
W rzeczywistości bardziej wydajne jest po prostu uruchomienie
exists
czeku niż zawracanie głowy wartością w skrócie, chociaż dla mnie ważną rzeczą jest tutaj po prostu koncepcja, że reprezentujesz zestaw tylko za pomocą kluczy skrótu. Ktoś również zwrócił uwagę, że używającundef
tutaj wartości jako wartości, zużyjemy mniej miejsca w pamięci, niż przypisując wartość. (A także generuje mniej zamieszania, ponieważ wartość nie ma znaczenia, a moje rozwiązanie przypisałoby wartość tylko do pierwszego elementu w haszu, a pozostawiło pozostałeundef
, a niektóre inne rozwiązania obracają koła zębate, aby zbudować tablicę wartości haszysz; całkowicie zmarnowany wysiłek).źródło
= ()
, nie= undef
tylko dla spójności w niejawnym używaniu undef dla wszystkich wartości, a nie tylko dla wszystkich po pierwszej. (Jak pokazano w tych komentarzach, zbyt łatwo jest zobaczyćundef
i pomyśleć, że można go po prostu zmienić na 1 i wpłynąć na wszystkie wartości skrótu.)Zwróć uwagę, że jeśli pisanie
if ( exists $hash{ key } )
nie jest dla Ciebie zbyt trudne (co wolę używać, ponieważ interesująca jest tak naprawdę obecność klucza, a nie prawdziwość jego wartości), możesz użyć krótkiego i słodkiegoźródło
Zawsze tak myślałem
foreach my $item (@array) { $hash{$item} = 1 }
był przynajmniej ładny i czytelny / możliwy do utrzymania.
źródło
Istnieje tutaj założenie, że najskuteczniejszy sposób wykonania wielu czynności „Czy tablica zawiera X?” Sprawdzenie polega na przekonwertowaniu tablicy na skrót. Wydajność zależy od rzadkich zasobów, często czasu, ale czasami przestrzeni, a czasami wysiłku programisty. Przynajmniej podwajasz ilość zajętej pamięci, przechowując jednocześnie listę i skrót listy. Poza tym piszesz bardziej oryginalny kod, który będziesz musiał przetestować, udokumentować itp.
Jako alternatywę, spojrzeć na module Lista :: MoreUtils, w szczególności funkcji
any()
,none()
,true()
ifalse()
. Wszystkie przyjmują blok jako warunek i listę jako argument, podobnie jakmap()
igrep()
:print "At least one value undefined" if any { !defined($_) } @list;
Przeprowadziłem szybki test, ładując połowę / usr / share / dict / words do tablicy (25000 słów), a następnie wyszukując jedenaście słów wybranych z całego słownika (co 5000-te słowo) w tablicy, używając zarówno tablicy -to-hash i
any()
funkcja z List :: MoreUtils.W Perlu 5.8.8 zbudowanym ze źródeł metoda tablica do skrótu działa prawie 1100 razy szybciej niż
any()
metoda (1300 razy szybciej w pakiecie Perl 5.8.7 z Ubuntu 6.06).Nie jest to jednak pełna historia - konwersja tablicy na skrót zajmuje około 0,04 sekundy, co w tym przypadku zabija wydajność czasową metody tablica do skrótu do 1,5x-2x szybciej niż
any()
metoda. Wciąż dobry, ale nie tak gwiezdny.Moje przeczucie jest takie, że metoda tablic-to-hash będzie
any()
lepsza w większości przypadków, ale czułbym się o wiele lepiej, gdybym miał bardziej solidne metryki (wiele przypadków testowych, przyzwoite analizy statystyczne, może niektóre duże O algorytmiczna analiza każdej metody itp.) W zależności od potrzeb List :: MoreUtils może być lepszym rozwiązaniem; jest z pewnością bardziej elastyczny i wymaga mniej kodowania. Pamiętaj, przedwczesna optymalizacja to grzech ... :)źródło
List::MoreUtils
może, ale nie musi, być odpowiednią metodą, w zależności od przypadku użycia. Twój przypadek użycia może mieć wiele wyszukiwań; inni mogą nie. Chodzi o to, że zarówno konwersja tablicy do skrótu, jak iList::MoreUtils
rozwiązanie podstawowego problemu określania członkostwa; znajomość wielu podejść pozwala wybrać najlepszą metodę dla konkretnego przypadku użycia.W perlu 5.10 jest operator ~~ zbliżony do magii:
sub invite_in { my $vampires = [ qw(Angel Darla Spike Drusilla) ]; return ($_[0] ~~ $vampires) ? 0 : 1 ; }
Zobacz tutaj: http://dev.perl.org/perl5/news/2007/perl-5.10.0.html
źródło
Warto również zwrócić uwagę na kompletność, moją zwykłą metodę robienia tego z 2 tablicami o tej samej długości
@keys
i@vals
którą wolałbyś być hashem ...my %hash = map { $keys[$_] => $vals[$_] } (0..@keys-1);
źródło
@keys-1
to$#keys
.Rozwiązanie Raldiego można zawęzić do tego (znak „=>” z oryginału nie jest konieczny):
my %hash = map { $_,1 } @array;
Ta technika może być również używana do przekształcania list tekstowych w skróty:
my %hash = map { $_,1 } split(",",$line)
Dodatkowo, jeśli masz taki wiersz wartości: „foo = 1, bar = 2, baz = 3”, możesz to zrobić:
my %hash = map { split("=",$_) } split(",",$line);
[EDYTUJ, aby uwzględnić]
Innym oferowanym rozwiązaniem (w dwóch liniach) jest:
my %hash; #The values in %hash can only be accessed by doing exists($hash{$key}) #The assignment only works with '= undef;' and will not work properly with '= 1;' #if you do '= 1;' only the hash key of $array[0] will be set to 1; @hash{@array} = undef;
źródło
Możesz także użyć Perl6 :: Junction .
use Perl6::Junction qw'any'; my @arr = ( 1, 2, 3 ); if( any(@arr) == 1 ){ ... }
źródło
Jeśli wykonujesz wiele operacji teoretycznych na zbiorach - możesz również użyć modułu Set :: Scalar lub podobnego. Następnie
$s = Set::Scalar->new( @array )
zbuduje zestaw dla Ciebie - i można wyszukać go z:$s->contains($m)
.źródło
Możesz umieścić kod w podprocedurze, jeśli nie chcesz zanieczyszczać swojej przestrzeni nazw.
my $hash_ref = sub{ my %hash; @hash{ @{[ qw'one two three' ]} } = undef; return \%hash; }->();
Albo jeszcze lepiej:
sub keylist(@){ my %hash; @hash{@_} = undef; return \%hash; } my $hash_ref = keylist qw'one two three'; # or my @key_list = qw'one two three'; my $hash_ref = keylist @key_list;
Jeśli naprawdę chciałeś przekazać odwołanie do tablicy:
sub keylist(\@){ my %hash; @hash{ @{$_[0]} } = undef if @_; return \%hash; } my @key_list = qw'one two three'; my $hash_ref = keylist @key_list;
źródło
%hash = map{ $_, undef } @keylist
#!/usr/bin/perl -w use strict; use Data::Dumper; my @a = qw(5 8 2 5 4 8 9); my @b = qw(7 6 5 4 3 2 1); my $h = {}; @{$h}{@a} = @b; print Dumper($h);
daje (zwróć uwagę, że powtórzone klawisze pobierają wartość z największej pozycji w tablicy - tj. 8-> 2 a nie 6)
$VAR1 = { '8' => '2', '4' => '3', '9' => '1', '2' => '5', '5' => '4' };
źródło
Możesz również sprawdzić Tie :: IxHash , który implementuje uporządkowane tablice asocjacyjne. Pozwoliłoby to na wykonanie obu typów wyszukiwania (hash i index) na jednej kopii danych.
źródło