Hash referencji to najbardziej elastyczna struktura danych dostępna w Perlu. Jest to najogólniej mówiąc tablica asocjacyjna przechowująca referencje do innych zmiennych (np. następnych hashy referencji, zmiennych skalarnych, tablicowych itd.). Poniżej kilka wybranych zagadnień dla praktyków.
Hash ref deklarujemy jak zmienną skalarną:
my $href;
W tym momencie jest ona gotowa do użycia - nie musimy deklarować żadnego fragmentu jej struktury. Wywołanie:
$href->{klucz1} = 'wartosc1'; # wartość skalarna
$href->{klucz2} = 'wartosc2';
spowoduje, że pod określonymi kluczami zostaną umieszczone odpowiednie wartości. Można także bez żadnych ograniczeń przypisać wartość/zmienną do automatycznie tworzonych hashy referencji kolejnych poziomów:
$href->{klucz1_1}->{klucz2_1} = $zmienna;
Zaznaczam od razu, że świadomie w przykładach nie zapisuję wartości klucza w apostrofach (
'klucz' zamiast
klucz). W Perlu nie ma typu danych int i string - jest typ skalarny, dlatego dopuszczalne jest zapisywanie prostych wartości klucza (jednowyrazowych) bez ciapków. Taki zapis spotykany jest często w dokumentacji, więc na pewno nie można go uznać za niepoprawny. Rzecz tylko w tym, żeby interpreter zrozumiał o co nam chodzi.
Co do samego sposobu dereferencji - istnieje wiele różnych opcji zapisu odwołania do danej wartości, wszystkie poniższe są równoważne:
$href->{kluczA}->{kluczB} = 'hello world';
print $href->{kluczA}->{kluczB}."\n";
print $$href{kluczA}{kluczB}."\n";
print ${$href->{kluczA}}{kluczB}."\n";
print ${${$href}{kluczA}}{kluczB}."\n";
Ze względu na czytelność kodu preferowany jest pierwszy sposób, w którym jawnie używany jest operator dereferencji (->). Możliwość tworzenia praktycznie dowolnych, zagnieżdżonych struktur to bardzo duży pozytyw, ale jednocześnie - niebezpieczeństwo zbudowania czegoś niezgodnego z intencjami. Musimy być świadomi tego, co umieszczamy jako wartość danego klucza. Przykładowo:
$href->{kluczA} = ['jeden', 'dwa'];
sprawi, że pod kluczem
kluczA będzie znajdowała się referencja do anonimowej tablicy, a nie sama tablica. Poniższa operacja nie przyniesie więc pożądanego skutku:
my @tablica = $href->{kluczA};
Prawidłowe są natomiast operacje:
my @tablica = @{$href->{kluczA}};
lub bezpośrednie odwołanie do elementu tablicy z hasha:
print $href->{kluczA}->[0];
Hash refs nadają się doskonale jako zmienne do przechowywania danych pobranych z BD. Wszystkie liczące się moduły dostępu do BD potrafią agregować resultsety właśnie do hashy referencji (DBI, Win32::*). Dzięki temu uzyskujemy kompleksową strukturę z bardzo szybkim skokiem do wartości - poprzez podanie określonego klucza.
Pętla po elementach hasha referencji wygląda np. tak:
foreach (keys %{$href}) {
# $_ jest kluczem
print $href->{$_}; # to jest wartość
}
Dodanie jednego hash ref do drugiego osiągniemy w następujący sposób:
%{$href1} = (%{$href1}, %{$href2});
Natomiast poniższa linijka kodu:
print scalar(keys %{$href});
wydrukuje liczbę elementów (par klucz - wartość) hasha.
Hash refs są na tyle elastyczne, że spokojnie możemy operować na ich elementach zapominając, że są one częściami większej struktury. Jesteśmy w stanie bez wysiłku przekazać referencję do samego elementu hasha:
my $referencja = \$href->{klucz}->{inny_klucz};
print $$referencja; # wartość
Jak widać możliwości są praktycznie nieograniczone, taka jest z resztą specyfika samego Perla. Na koniec banalna zagadka - czym jest wartość wydrukowana na ekranie przez poniższy kod:
print \$href;
Odpowiedź: to referencja do zmiennej, która przechowuje referencję do hasha referencji. Brzmi skomplikowanie? Tylko pozornie.