NAZWA¶
perlref - odwołania i zagnieżdżone struktury danych w Perlu
UWAGA¶
To jest pełna dokumentacja dotycząca wszelkich aspektów
odwołań. Krótszy, wprowadzający wstęp to
najistotniejszych cech znajdziesz w podręczniku
perlreftut(1).
OPIS¶
Uwaga! To tłumaczenie może być nieaktualne!
Przed wersją 5 Perla przedstawianie złożonych struktur
danych było trudne, gdyż wszystkie odwołania
musiały być symboliczne -- a nawet wtedy ciężko
było odnosić się do zmiennej zamiast do pozycji w tablicy
symboli. Obecnie Perl nie tylko ułatwia posługiwanie się
symbolicznymi odwołaniami do zmiennych, ale także pozwala na
użycie "stałych" odwołań (hard
references) do dowolnego kawałka danych lub kodu. Stałe
dowiązanie może być przechowywane w dowolnym skalarze.
Ponieważ tablice i tablice asocjacyjne (hasze) zawierają
skalary, to możesz teraz łatwo budować tablice tablic,
tablice haszy, hasze tablic, tablice haszy funkcji i tak dalej.
Odwołania stałe są sprytne -- utrzymują za ciebie
liczniki odwołań, automatycznie zwalniając rzecz, do
której odnosi się odwołanie, jeśli jej licznik
odwołań zejdzie do zera. (Uwaga: liczniki odwołań
dla wartości w strukturach danych odnoszących się do
samych na siebie (self-referential) lub strukturach cyklicznych mogą
nie schodzić do zera bez pewnej drobnej pomocy; patrz sekcja
Two-Phased Garbage Collection w podręczniku
perlobj(1),
zawierająca bardziej szczegółowy opis.) Jeśli taka
rzecz jest obiektem, to obiekt jest niszczony. Więcej o obiektach
znajdziesz w podręczniku
perlobj(1). (W pewnym sensie, wszystko
w Perlu jest obiektem, ale zwykle rezerwujemy to słowo dla
odwołań do obiektów, które zostały
oficjalnie "pobłogosławione" ("blessed")
[tj.zakwalifikowane jako obiekty] w pakiecie klasy.)
Odwołania symboliczne są nazwami zmiennych lub innych
obiektów; zupełnie tak jak dowiązania symboliczne
(symbolic links) w uniksowym systemie plików zawierają
wyłącznie nazwę pliku. Notacja *glob jest rodzajem
odwołania symbolicznego. (Odwołania symboliczne nazywane
są czasami "soft references" [odwołaniami
miękkimi, w przeciwieństwie do "hard
r."-"twardych"], ale proszę nie nazywaj ich tak;
odwołania są wystarczająco zbijające z
pantałyku bez zbędnych synonimów.)
W przeciwieństwie do nich, odwołania stałe
przypominają dowiązania stałe (hard links) uniksowego
systemu plików: służą do udostępniania
obiektu bez zwracania uwagi na to, jaka jest jego (inna) nazwa. Tam, gdzie
użyto słowa "odwołanie" bez przymiotnika, jak w
poniższym akapicie, mówi się zwykle o odwołaniu
stałym.
W Perlu odwołania są łatwe w użyciu. Jest tylko
jedna nadrzędna zasada: Perl nie wykonuje niejawnego odwoływania
bądź dereferowania odwołań. [Dereferencing:
odniesienie się z powrotem do obiektu, rzeczy na którą
wskazuje odwołanie]. Gdy skalar przechowuje odwołanie, to zawsze
zachowuje się jak zwykły skalar. Nie zaczyna magicznie
być tablicą, haszem czy procedurą; musisz wprost
nakazać takie zachowanie, wykonując dereferencję.
Tworzenie odwołań
Odwołania mogą być tworzone na kilka sposobów.
- 1.
- Przez zastosowanie operatora odwróconego ukośnika do
zmiennej, procedury lub wartości. (Działa to bardzo podobnie
do operatora & (zwracającego adres) w języku C.)
Zauważ, że konstrukcja ta tworzy KOLEJNE
odwołanie do zmiennej, gdyż w tablicy symboli istnieje
już odwołanie do tej zmiennej. Jednak odwołanie z
tablicy symboli może zniknąć, a nadal będziesz
mieć odwołanie, które zwrócił
odwrócony ukośnik. Oto kilka przykładów:
$scalarref = \$foo;
$arrayref = \@ARGV;
$hashref = \%ENV;
$coderef = \&handler;
$globref = \*foo;
Przy użyciu operatora odwróconego ukośnika nie jest
możliwe utworzenie prawdziwego odwołania do uchwytu IO
(uchwytu pliku lub katalogu). Możesz co najwyżej
uzyskać odwołanie do typeglob będącego
faktycznie pełnym wpisem w tablicy symboli. (Przeczytaj jednak
poniżej objaśnienie składni *foo{COŚ}.) Mimo
to, możesz nadal używać typeglob i
odwołań do nich jakby były one uchwytami IO.
- 2.
- Odwołanie do anonimowej tablicy można stworzyć
posługując się nawiasami kwadratowymi:
$arrayref = [1, 2, ['a', 'b', 'c']];
Utworzyliśmy odwołanie do anonimowej tablicy trzech
elementów, której ostatni element jest z kolei
odwołaniem do innej anonimowej tablicy trzech elementów.
(Dostęp do niej umożliwi opisana dalej składnia
tablic wielowymiarowych. Na przykład, dla powyższego
przykładu $arrayref->[2][1] zwraca wartość
"b".)
Zauważ, że stworzenie odwołania do listy wyliczanej nie
jest tym samym, co użycie nawiasów kwadratowych. Jest to
natomiast tym samym, co stworzenie listy odwołań!
@list = (\$a, \@b, \%c);
@list = \($a, @b, %c); # to samo!
W przypadku szczególnym, \(@foo) zwraca listę
odwołań do zawartości @foo, nie zaś
odwołanie do samej @foo. Podobnie jest dla %foo, z wyjątkiem
tego, że odwołania-klucze odnoszą się do kopii
(gdyż klucze są łańcuchami znakowymi, a nie
dowolnymi skalarami).
- 3.
- Odwołanie do anonimowej tablicy asocjacyjnej (hasza) można
utworzyć używając nawiasów klamrowych:
$hashref = {
'Adam' => 'Ewa',
'Clyde' => 'Bonnie',
};
Powyższe konstruktory anonimowych haszy i tablic można
swobodnie łączyć. Umożliwia to otrzymywanie
dowolnie skomplikowanych struktur. Opisana składnia
wielowymiarowych tablic/haszy działa także dla nich.
Wartości w powyższym przykładzie były
literałami, ale równie dobrze mogłyby być
zmiennymi czy wyrażeniami, gdyż perlowe operatory
przypisania (nawet wewnątrz local() czy my())
są wykonywalnymi instrukcjami, a nie jedynie deklaracjami dla fazy
kompilacji.
Ponieważ nawiasy klamrowe służą do kilku innych
rzeczy, a także do tworzenia BLOKów, możesz
być czasem zmuszony do uniknięcia dwuznaczności tych
nawiasów na początku instrukcji. Wystarczy wówczas
umieszczenie przed nimi + lub return, by Perl zorientował
się, że otwierający nawias klamrowy nie rozpoczyna
BLOKu. Oszczędność i zalety mnemoniczne użycia
nawiasów klamrowych warte są takiego sporadycznego
zamieszania.
Na przykład, jeśli chciałbyś, by funkcja
tworzyła nowy hasz i zwracała odwołanie do niego, to
masz takie możliwości:
sub hashem { { @_ } } # źle, ale bez komunikatu o błędzie
sub hashem { +{ @_ } } # ok
sub hashem { return { @_ } } # ok
Z drugiej strony, jeśli chcesz drugiego znaczenia nawiasów
(blok), zrób tak:
sub showem { { @_ } } # dwuznaczne (obecnie ok, ale może się zmienić)
sub showem { {; @_ } } # ok
sub showem { { return @_ } } # ok
Zwróć uwagę, że początkowe +{ i {; zawsze
służą do wykluczenia dwuznaczności
wyrażenia, aby znaczyło albo odwołanie do HASZa albo
BLOK.
- 4.
- Można utworzyć odwołanie do anonimowej procedury
używając sub bez nazwy procedury:
$coderef = sub { print "Bums!\n" };
Zwróć uwagę na obecność średnika.
Poza faktem, że wewnętrzny kod nie jest wykonywany
natychmiast, sub {} jest bardziej operatorem niż deklaracją,
podobnie zresztą jak do{} czy eval{}. (Jednak, niezależnie
od tego, ile razy wykonasz powyższą linię (chyba
że jesteś wewnątrz eval("...")), $coderef
wciąż będzie zawierać odwołanie do
TEJ SAMEJ anonimowej procedury.)
Procedury anonimowe działają jak zamknięcia (closures)
w odniesieniu do zmiennych my(), to znaczy, zmiennych widocznych
leksykalnie w bieżącym zakresie. Zamknięcie jest
pojęciem ze świata Lispa, mówiącym, że
jeśli zdefiniujesz anonimową funkcję w konkretnym
kontekście leksykalnym, to będzie ona działać
w tym kontekście nawet jeśli została wywołana
poza nim.
Mówiąc po ludzku, jest to zabawny sposób
przesyłania argumentów do procedury zarówno gdy
ją definiujesz jak i wtedy gdy ją wywołujesz.
Przydaje się to do tworzenia małych fragmentów kodu
do późniejszego uruchamiania, jak np. wywołania
wsteczne (callbacks). Przy ich pomocy możesz robić nawet
rzeczy zorientowane obiektowo, choć Perl zapewnia już
odmienny mechanizm operowania obiektami --patrz podręcznik
perlobj(1).
Możesz również myśleć o
zamknięciach jak o sposobie pisania szablonów bez
używania eval. (Faktycznie, w wersji 5.000, eval było
jedyną metodą uzyskania zamknięć.
Jeśli posługujesz się zamknięciami,
możesz potrzebować "require 5.001".)
A to mały przykład tego, jak działają
zamknięcia:
sub newprint {
my $x = shift;
return sub { my $y = shift; print "$x, $y!\n"; };
}
$h = newprint("Howdy");
$g = newprint("Greetings");
# czas mija...
&$h("world");
&$g("earthlings");
Drukuje to
Howdy, world!
Greetings, earthlings!
Zwróć uwagę szczególnie na to, że $x
nadal odnosi się do wartości przesłanej do
newprint(), mimo że zmienna "my $x"
pozornie wyszła poza swój zakres, w momencie gdy
wywołano anonimową procedurę. O to
właśnie chodzi w zamknięciu.
Przy okazji: odnosi się do tylko do zmiennych leksykalnych. Zmienne
dynamiczne działają nadal tak jak zawsze. Zamknięcie
nie jest czymś, o co musiałaby się martwić
większość programistów Perla.
- 5.
- Odwołania często zwracane są przez specjalne
procedury zwane konstruktorami. Obiekty w Perlu są po prostu
odwołaniami do specjalnego rodzaju obiektu, który wie z
którym pakietem jest związany. Konstruktory są
specjalnymi procedurami, które wiedzą jak utworzyć to
powiązanie. Robią to zaczynając od zwykłego
odwołania, i pozostaje ono zwykłym odwołaniem nawet
wtedy gdy jest równocześnie obiektem. Konstuktory
często nazywane są new() i wywoływane nie
wprost:
$objref = new Psisko (Ogon => 'krótki', Uszy => 'długie');
Ale nie muszą być:
$objref = Psisko->new(Ogon => 'krótki', Uszy => 'długie');
use Term::Cap;
$terminal = Term::Cap->Tgetent( { OSPEED => 9600 });
use Tk;
$main = MainWindow->new();
$menubar = $main->Frame(-relief => "raised",
-borderwidth => 2)
- 6.
- Odwołania odpowiedniego typu mogą być
powoływane do istnienia jeśli dereferencjonujesz je w
kontekście zakładającym, że istnieją.
Ponieważ jeszcze nie mówiliśmy o dereferencji, nie
możemy na razie pokazać przykładów.
- 7.
- Odwołanie może być utworzone przy pomocy specjalnej
składni, uroczo zwanej składnią *foo{COŚ}.
*foo{COŚ} zwraca odwołanie do przegródki COŚ w
*foo (które jest pozycją w tablicy symboli
przechowującą wszystko znane jako foo.)
$scalarref = *foo{SCALAR};
$arrayref = *ARGV{ARRAY};
$hashref = *ENV{HASH};
$coderef = *handler{CODE};
$ioref = *STDIN{IO};
$globref = *foo{GLOB};
Wszystkie powyższe wyrażenia są oczywiste, z
wyjątkiem *foo{IO}. Zwraca ono uchwyt IO, używany jako
uchwyt pliku (patrz opis open w podręczniku perlfunc(1)),
gniazdo (opis socket oraz socketpair w perlfunc(1)) lub uchwyt
katalogu (opendir w perlfunc(1)). Dla zgodności z
poprzednimi wersjami Perla, *foo{UCHWYTPLIKU} jest synonimem *foo{IO}.
*foo{COŚ} zwraca undef jeśli dane COŚ jeszcze nie
było używane, z wyjątkiem dla skalarów.
Jeśli nie używano jeszcze $foo, *foo{SKALAR} zwraca
odwołanie do anonimowego skalara. W przyszłych wersjach
może się to zmienić.
*foo{IO} jest alternatywą dla mechanizmu \*UCHWYTU opisanego w sekcji
Typeglobs and Filehandles podręcznika perldata(1), a
służącego do przesyłania uchwytów
plików do i z procedur lub przechowywania w większych
strukturach danych. Jego wadą jest to, że nie utworzy za
Ciebie nowego uchwytu pliku. Zaletą zaś, że nie
ryzykujesz więcej niż zamierzałeś przy
przypisaniem typeglob, choć jeśli wykonasz przypisanie do
skalara zamiast do typeglob, to też dobrze.
splutter(*STDOUT);
splutter(*STDOUT{IO});
sub splutter {
my $fh = shift;
print $fh "her um well a hmmm\n";
}
$rec = get_rec(*STDIN);
$rec = get_rec(*STDIN{IO});
sub get_rec {
my $fh = shift;
return scalar <$fh>;
}
Posługiwanie się odwołaniami
To tyle o tworzeniu odwołań. Teraz pewnie nie możesz
się doczekać wiedzy jak posługiwać się
odwołaniami, by móc wrócić do swych
leżących odłogiem danych. Oto kilka podstawowych
sposobów.
- 1.
- Wszędzie, gdzie postawiłbyś identyfikator (lub
łańcuch identyfikatorów) jako
część nazwy zmiennej czy procedury, możesz
zastąpić identyfikator prostą zmienną
skalarną zawierającą odwołanie poprawnego
typu:
$bar = $$scalarref;
push(@$arrayref, $nazwapliku);
$$arrayref[0] = "styczeń";
$$hashref{"KLUCZ"} = "WARTOŚĆ";
&$coderef(1,2,3);
print $globref "wynik\n";
Ważne jest, by zrozumieć, że nie NIE wykonujemy
tu specjalnie dereferencji $arrayref[0] czy $hashref{"KLUCZ"}.
Dereferencja zmiennej skalarnej odbywa się PRZED
przeszukaniem klucza (indeksu tablicy). Wszystko bardziej skomplikowane
niż dereferencja prostej zmiennej skalarnej wymaga użycia
niżej opisanych metod 2 lub 3. Jednak określenie
"prosty skalar" obejmuje też identyfikator, który
sam używa rekurencyjnie metody 1. Zatem poniższe drukuje
"witaj".
$refrefref = \\\"witaj";
print $$$$refrefref;
- 2.
- Wszędzie, gdzie postawiłbyś identyfikator (lub
łańcuch identyfikatorów) jako
część nazwy zmiennej czy procedury, możesz
zastąpić identyfikator BLOKiem zwracającym
odwołanie poprawnego typu. Inaczej mówiąc, poprzednie
przykłady mogą zostać zapisane tak:
$bar = ${$scalarref};
push(@{$arrayref}, $nazwapliku);
${$arrayref}[0] = "styczeń";
${$hashref}{"KLUCZ"} = "WARTOŚĆ";
&{$coderef}(1,2,3);
$globref->print("wynik\n"); # jeśli załadowano IO::Handle
Niewątpliwie, użycie nawiasów klamrowych w tym
przypadku nie jest zbyt mądre, ale BLOK może zawierać
dowolne wyrażenie, w szczególności wyrażenia
indeksowane:
&{ $dispatch{$index} }(1,2,3); # wywołaj właściwą obsługę
Z powodu możliwości pomijania nawiasów klamrowych dla
prostych przypadków $$x, ludzie często
popełniają błąd postrzegania symboli
dereferencji jako prawdziwych operatorów i zastanawiają
się nad ich priorytetem. Gdyby nimi były,
mógłbyś używać zwykłych
nawiasów zamiast klamrowych. Tak jednak nie jest. Rozważ
poniższą różnicę: przypadek 0 jest
skróconą wersją przypadku 1, NIE przypadku 2:
$$hashref{"KLUCZ"} = "WARTOŚĆ"; # przypadek 0
${$hashref}{"KLUCZ"} = "WARTOŚĆ"; # przypadek 1
${$hashref{"KLUCZ"}} = "WARTOŚĆ"; # przypadek 2
${$hashref->{"KLUCZ"}} = "WARTOŚĆ"; # przypadek 3
Przypadek 2 jest również mylący, gdyż odnosi
się do zmiennej o nazwie %hashref, nie zaś dereferencjonuje
poprzez $hashref hasza, na który wydawałoby się
wskazuje skalar. To robi przypadek 3.
- 3.
- Wywołania procedur i poszukiwanie poszczególnych
elementów tablic pojawiają się wystarczająco
często, by zastosowanie do nich metody 2 stało się
niewygodne. Jako formę "osłodzenia
składni", przykłady z metody 2 można
zapisać:
$arrayref->[0] = "styczeń"; # element tablicy
$hashref->{"KLUCZ"} = "WARTOŚĆ"; # element hasza
$coderef->(1,2,3); # wywołanie procedury
Lewa strona strzałki może być dowolnym
wyrażeniem zwracającym odwołanie,
łącznie z uprzednią dereferencją.
[Ułatwia to operowanie odwołaniami do zmiennych
zawierających kolejne odwołania, jak poniżej].
Zauważ, że $array[$x] NIE jest tu tym samym co
$array->[$x]:
$array[$x]->{"foo"}->[0] = "styczeń";
Jest to jeden z przypadków wspomnianych wcześniej, gdzie
odwołania zaistnieją, gdy zostaną użyte w
kontekście l-wartości. Przed tą instrukcją,
element $array[$x] mógł być niezdefiniowany. W takim
przypadku, jest on definiowany automatycznie z nadaniem mu wartości
-- odwołania do hasza, tak że możemy
poszukiwać w haszu elementu o kluczu "foo". Podobnie
klucz $array[$x]->{"foo"} zostanie automatycznie zdefiniowany
z równoczesnym nadaniem wartości -- odwołania do
tablicy, zatem będzie można w niej odnaleźć
[0]. Proces ten zwany jest autovivification (automatyczne
ożywianie).
Jeszcze jedno. POMIĘDZY indeksami umieszczonymi w nawiasach
klamrowych strzałka jest opcjonalna, zatem możemy
skrócić powyższy zapis do:
$array[$x]{"foo"}[0] = "styczeń";
Co, w szczególnym przypadku działania tylko na zwykłych
tablicach, daje tablice wielowymiarowe z zapisem jak w C:
$score[$x][$y][$z] += 42;
No dobrze, tak naprawdę, nie całkiem jak tablice w C. C nie
wie, jak poszerzać tablice na żądanie. Perl to
potrafi.
- 4.
- Jeżeli odwołanie jest odwołaniem do obiektu, to
prawdopodobnie istnieją metody dostępu do wskazywanych przez
nie rzeczy, i powinieneś zapewne z nich skorzystać, chyba
że jesteś w pakiecie klasy definiującej metody tego
obiektu i pracujesz nad nimi. Inaczej mówiąc,
bądź tak dobry i nie naruszaj hermetyzacji bez istotnego
powodu. Perl nie wymusza hermetyzacji. Nie jesteśmy tu
totalitarystami. Oczekujemy jednak zachowania podstawowych zasad
uprzejmości.
Można posłużyć się operatorem
ref() do
stwierdzenia, na jaki typ rzeczy wskazuje odwołanie. Zobacz
podręcznik
perlfunc(1).
Operator
bless() może być używany do
powiązania obiektu, na który wskazuje odwołanie, z
pakietem funkcjonującym jako klasa obiektowa. Zobacz podręcznik
perlobj(1).
Typeglob może być dereferencjowane w ten sam sposób jak
odwołanie, gdyż składnia dereferencji zawsze wskazuje na
pożądany rodzaj odwołania. Zatem ${*foo} i ${\$foo}
wskazują na tę samą zmienną skalarną.
A oto sztuczka do interpolacji wywołania procedury w
łańcuchu:
print "Procedura mysub tym razem zwróciła @{[mysub(1,2,3)]} .\n";
Działa to w tak, że gdy @{...} znalezione zostanie wewnątrz
łańcucha w cudzysłowach to zostanie potraktowane jako
blok. Blok ten tworzy odwołanie do jednoelementowej anonimowej tablicy
zawierającej wynik wywołania mysub(1,2,3) [odwołanie to
utworzone będzie dzięki nawiasom kwadratowym]. Zatem cały
blok zwraca odwołanie do tablicy, która następnie podlega
dereferencji powodowanej przez @{...}. Jej wartość, jako
umieszczona w łańcuchu w cudzysłowach podlega
interpolacji w napis. Takie szykany przydają się także w
dowolnych wyrażeniach:
print "That yields @{[$n + 5]} widgets\n";
Odwołania symboliczne
Powiedzieliśmy, że niezdefiniowane cele odwołania w razie
potrzeby zaistnieją [podczas dereferencji]. Nie mówiliśmy
jednak, co się dzieje, gdy wartość użyta jako
odwołanie jest już zdefiniowana, ale
NIE JEST
odwołaniem stałym. Jeżeli użyjesz odwołania
w takim przypadku, to będzie ono potraktowane jak odwołanie
symboliczne. To znaczy, wartością skalara zostanie
NAZWA
zmiennej a nie bezpośrednie dowiązanie do (być
może anonimowej) wartości.
Niektórzy często spodziewają się, że
działa to jakoś tak. I rzeczywiście.
$name = "foo";
$$name = 1; # ustawia $foo
${$name} = 2; # ustawia $foo
${$name x 2} = 3; # ustawia $foofoo
$name->[0] = 4; # ustawia $foo[0]
@$name = (); # czyści @foo
&$name(); # wywołuje &foo() (jak w Perl 4)
$pack = "THAT";
${"${pack}::$name"} = 5; # ustawia $THAT::foo bez rozwinięcia(eval)
Jest to bardzo silne narzędzie, ale nieco niebezpieczne, gdyż
możliwe jest, ze szczerym zamiarem użycia odwołania
stałego, przypadkowe użycie symbolicznego. Możesz
się przed tym uchronić pisząc:
use strict 'refs';
a dla reszty otaczającego bloku będą dozwolone tylko
odwołania stałe. Wewnętrzny blok może się
temu sprzeciwić przy pomocy
no strict 'refs';
Dla odwołań symbolicznych widoczne są tylko zmienne pakietu
(globalne, nawet jeśli lokalnie). Zmienne leksykalne (deklarowane przy
pomocy
my()) nie zawierają się w tablicy symboli, zatem
są niewidoczne dla tego mechanizmu. Na przykład:
local $wartosc = 10;
$ref = "wartosc";
{
my $wartosc = 20;
print $$ref;
}
Nadal będzie drukować 10, a nie 20. Pamiętaj, że
local() działa na zmienne pakietu, które dla samego
pakietu wszystkie są "globalne".
Odwołania niezbyt symboliczne
Nową cechą poprawiającą czytelność,
wprowadzoną w perlu wersji 5.001, jest to, że nawiasy
wokół odwołania symbolicznego zachowują się
jak znaki cudzysłowu, czyli tak, jakby zawsze zawierały
wewnątrz łańcuch. To znaczy, że
$push = "pop on ";
print "${push}over";
miało zawsze znaczenie wydrukowania "pop on over", bez
względu na fakt, że "push" jest słowem
zarezerwowanym. Zostało to uogólnione tak, by
działać również poza cudzysłowami, zatem
print ${push} . "over";
a nawet
print ${ push } . "over";
mają ten sam rezultat. (Spowodowałoby to błąd
składni w Perlu 5.000, choć Perl 4 dopuszcza coś takiego
w postaci bez odstępów.) Zauważ, że konstrukcja ta
nie nie jest uważana za odwołanie symboliczne gdy
używasz strict refs:
use strict 'refs';
${ bareword }; # dobrze, znaczy $bareword.
${ "bareword" }; # błąd, odwołanie symboliczne.
Podobnie, z powodu wszelkiego indeksowania przy pomocy pojedynczych
słów, zastosowaliśmy tę samą
regułę do każdego z gołych słów
użytego do indeksowania hasza. Zatem teraz, zamiast
$array{ "aaa" }{ "bbb" }{ "ccc" }
możesz napisać po prostu
$array{ aaa }{ bbb }{ ccc }
i nie martwić się o to, czy indeksy są słowami
zarezerwowanymi. W tych rzadkich przypadkach, gdy chcesz zrobić
coś w rodzaju
$array{ shift }
możesz wymusić interpretację słowa jako
zarezerwowanego dodając cokolwiek, co zrobi zeń więcej
niż gołe słowo:
$array{ shift() }
$array{ +shift }
$array{ shift @_ }
Przełącznik
-w będzie Cię ostrzegał,
jeśli zinterpretuje słowo zarezerwowane jako
łańcuch. Nie będzie jednak ostrzegał o
użyciu słów pisanych małymi literami, gdyż
łańcuch jest faktycznie cytowany.
Pseudo-hasze: Używanie tablicy jak hasza
OSTRZEŻENIE: Niniejsza sekcja opisuje cechę
eksperymentalną. W przyszłych wersjach szczegóły
mogą ulec zmianie bez powiadomienia.
Począwszy od Perla 5.005 możesz w pewnych kontekstach
posługiwać się odwołaniem do tablicy, mimo
że normalnie wymagają one odwołania do hasza. Pozwala to
na dostęp do elementów tablicy przy użyciu nazw
symbolicznych, tak jakby były one polami struktury.
Żeby to zadziałało tablica musi zawierać
dodatkową informację. Pierwszym elementem tablicy powinno
być odwołanie do hasza odwzorowującego nazwy pól
na indeksy tablicy. Oto przykład:
$struct = [{foo => 1, bar => 2}, "FOO", "BAR"];
$struct->{foo}; # to samo, co $struct->[1], tj. "FOO"
$struct->{bar}; # to samo, co $struct->[2], tj. "BAR"
keys %$struct; # zwróci ("foo", "bar") w jakiejś kolejności
values %$struct; # zwróci ("FOO", "BAR") w jakiejś kolejności
while (my($k,$v) = each %$struct) {
print "$k => $v\n";
}
Jeśli spróbujesz usunąć klucze z takiego
pseudo-hasza lub będziesz próbował
sięgnąć do nieistniejących pól, perl
zgłosi wyjątek. W celu poprawy wydajności, Perl
może też wykonać na etapie kompilacji tłumaczenie
nazw pól na odpowiadające im indeksy tablicy dla opisanych
odwołań. Patrz podręcznik
fields(3).
Szablony funkcji
Jak wyjaśniono powyżej, zamknięcie jest anonimową
funkcją z dostępem do zmiennych leksykalnych widocznych podczas
jej kompilacji. Zachowuje ona dostęp do tych zmiennych nawet wtedy, gdy
jest wykonywana później, tak jak funkcja obsługi
sygnału (signal handler) czy wywołanie wsteczne Tk.
Posługiwanie się zamknięciem jako szablonem funkcji
umożliwia tworzenie wielu podobnie działających funkcji.
Załóżmy, że potrzebujesz funkcji o nazwach
pochodzących od różnych kolorów
zmieniających czcionkę HTML.
print "Hej, ", red("uważaj"), "na to ", green("światło");
Funkcje
red() i
green() będą bardzo podobne. By je
stworzyć, przypiszemy zamknięcie do typeglob nazwy funkcji,
którą próbujemy skonstruować.
@kolory = qw(red blue green yellow orange purple violet);
for my $nazwa (@kolory) {
no strict 'refs'; # pozwól na operowanie tablicą symboli
*$nazwa = *{uc $nazwa} = sub { "<FONT COLOR='$nazwa'>@_</FONT>" };
}
Teraz wszystkie te funkcje będą istnieć niezależnie
od siebie. Możesz wywoływać
red(),
RED(),
blue(),
BLUE(),
green(), etc. Technika ta zarówno
skraca czas kompilacji jak i zmniejsza zużycie pamięci, jest
też mniej narażona na błędy, gdyż kontrola
składni odbywa się podczas kompilacji. Istotne jest, by wszelkie
zmienne w anonimowej procedurze były zmiennymi leksykalnymi by
stworzyć poprawne zamknięcie. Z tego powodu użyto my dla
zmiennej sterującej pętli.
Jest to jedno z jedynych miejsc, gdzie dostarczenie prototypu do
zamknięcia ma sens. Jeśli chciałbyś
narzucić kontekst skalarny dla argumentów powyższych,
przykładowych funkcji (pewnie nie najlepszy pomysł w tym
przypadku), możesz zapisać to inaczej:
*$nazwa = sub ($) { "<FONT COLOR='$nazwa'>$_[0]</FONT>" };
Jednakże, ponieważ sprawdzanie protypów odbywa się
podczas kompilacji, powyższe przypisanie zostanie wykonane za
późno, by było przydatne. Mógłbyś to
obejść przez włożenie całej pętli
przypisań do wnętrza bloku BEGINu, wymuszając wykonanie
go w czasie kompilacji.
Dostęp do zmiennych leksykalnych zmieniających typ -- jak te w
pętli for powyższego przykładu-- działa
wyłącznie z zamknięciami, a nie z procedurami w
ogóle. Zatem w przypadku ogólnym, procedury nazwane nie
zagnieżdżają się prawidłowo, choć
robią to procedury anonimowe. Jeśli nawykłeś do
używania zagnieżdżonych procedur z własnymi
prywatnymi zmiennymi w innych językach programowania, to w Perlu
będziesz musiał nad trochę popracować. Intuicyjna
metoda kodowania tego typu rzeczy spowoduje tajemnicze ostrzeżenia
``will not stay shared'' (nie pozostanie wspólne). To, na
przykład, nie zadziała:
sub zewn {
my $x = $_[0] + 35;
sub wewn { return $x * 19 } # ŹLE
return $x + wewn();
}
Obejście jest następujące:
sub zewn {
my $x = $_[0] + 35;
local *wewn = sub { return $x * 19 };
return $x + wewn();
}
Teraz
wewn() może być wywołana tylko z
wnętrza
zewn(), z powodu tymczasowego przypisania
zamknięcia (procedury anonimowej). Ale kiedy jest wywoływana, to
ma zwykły dostęp do zmiennej leksykalnej $x z zakresu procedury
zewn().
Ma to interesujący skutek tworzenia funkcji lokalnych względem
innych funkcji, co normalnie nie jest obsługiwane w Perlu.
OSTRZEŻENIE¶
Nie możesz (w użyteczny sposób)
posłużyć się odwołaniem jako kluczem hasza.
Zostanie ono zamienione na łańcuch:
$x{ \$a } = $a;
Jeśli spróbujesz zdereferencjonować klucz, nie otrzymasz
odwołania stałego a łańcuch i nie uzyskasz tego,
co próbowałeś. W zamian można napisać
coś podobnego do:
$r = \@a;
$x{ $r } = $r;
a następnie użyć
values(), co zwróci
rzeczywiste odwołania, zamiast użycia
keys(), gdyż
klucze odwołaniami nie będą.
Standardowy moduł Tie::RefHash umożliwia wygodny sposób
obejścia tego problemu.
ZOBACZ TAKŻE¶
Poza oczywistą dokumentacją, pouczająca może
być analiza kodu źródłowego. Kilka raczej
patologicznych przykładów użycia odwołań
znajdziesz w teście regresji
t/op/ref.t w katalogu
źródeł Perla.
Zobacz również podręczniki
perldsc(1) i
perllol(1), opisujące posługiwanie się
odwołaniami do tworzenia złożonych struktur danych, oraz
perltoot(1),
perlobj(1) i
perlbot(1) opisujące ich
użycie do tworzenia obiektów.
Powyższe tłumaczenie pochodzi z nieistniejącego już
Projektu Tłumaczenia Manuali i
może nie być
aktualne. W razie zauważenia różnic między
powyższym opisem a rzeczywistym zachowaniem opisywanego programu lub
funkcji, prosimy o zapoznanie się z oryginalną
(angielską) wersją strony podręcznika za pomocą
polecenia:
- man --locale=C 1 perlref
Prosimy o pomoc w aktualizacji stron man - więcej informacji można
znaleźć pod adresem
http://sourceforge.net/projects/manpages-pl/.