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/.