Balíky

Jednou z nutností při programování je nějakým způsobem zajistit jednotlivým částem programů určitou autonomitu. To znamená přidělit jim prostor, který mohou využívat a který nezasahuje do svého okolí. Tento prostor se nazýva prostor jmen a spadají do něj všechna jména proměnných, podprogramů, ovladačů a formátů používaných v tomto prostoru. Tato jména nikoho z okolí nemusejí zajímat a ani v části programu s vlastním prostorem jmen není třeba řešit otázku, zda někde jinde je konkrétní jméno již použito. Každé jméno v prostoru jmen je unikátní, tzn. že díky jmenným prostorům je možné mít dvě proměnné stejného jména, ovšem tyto proměnné musejí náležet do jiného prostoru jmen. Příslušností jména do svého prostoru se zabrání zaměňování jmen z různých prostorů.

V Perlu ve skutečnosti neexistují globální jména (proměnných, podprogramů, ovladačů a formátů). Ve skutečnosti každé jméno náleží do nějakého prostoru jmen. Tento prostor jmen se nazývá balík a každé jméno je navíc ještě specifikováno jménem balíku (prostoru jmen), kam náleží. Některé proměnné se mohou zdát jako globální, ovšem tyto patří do balíku main. Ke jménu proměnné se potom přidá jméno balíku a dvě dvojetčky.

$Balík::promenna
Balík::podprogram

Dříve se používalo znaku apostrof a kvůli zpětné kompatibilitě je tento zápis i nadále možný.

Není-li jméno balíku uvedeno, nebo je-li použito zápisu $::proměnná, pracuje se s balíkem main.

Jedinými globálními proměnnými jsou vestavěné proměnné typu $_, $? apod. Ty se nacházejí v balíku main a je možné je bez celé specifikace používat kdekoliv.

Deklarace a platnost balíku

Balík se deklaruje pomocí klíčového slova package následovaného jménem balíku. Platnost balíku končí další deklarací balíku, dosažením konce souboru, konce bloku nebo bloku eval, kde byl balík deklarován (platí zde stejná pravidla jako pro platnost jmen při použití operátorů my a local).

Jméno aktuálního balíku je dostupné pomocí speciálního symbolu __PACKAGE__.

print __PACKAGE__;   # vytiskne 'main'

{ package A;
  print __PACKAGE__; # vytiskne 'A'
}

print __PACKAGE__;   # vytiskne 'main'

package A;
$x = 10;    # proměnná $A::x
print $x;   # vytiskne 10

package B;
$x = 20;    # proměnná $B::x
print $x;   # vytiskne 20

package A;  # proměnná $A::x, už je nastavená
print $x;   # vytiskne 10


package A;
$x = '123';    # proměnná $A::x

$B::x = 'abc'; # nastavení proměnné v jiném balíku 
               # (proměnná $x v balíku B)
                  
sub B::f {     # definice podprogramu v jiném balíku
               # (podprogram f v balíku B)
  print "Hodnota x ve funkci f = $x";
}

package B;
print $x;      # proměnná $B::x, vytiskne se 'abc'

sub ff {       # podprogram B::ff
  print "Hodnota x ve funkci ff = $x";
}

f;             # vytiskne se 'Hodnota x ve funkci f = 123'
ff;            # vytiskne se 'Hodnota x ve funkci ff = abc'

Vhnízděné balíky

Objeví-li se ve jméně balíku symbol ::, setkáváme se s pojmem vhnízděné balíky. Nutně to však neznamená, že takto pojmenované balíky musejí mít něco společného. Konvencí ale je, že když spolu balíky souvisejí, jsou umístěny společně v nějakém adresáři a mají stejné pojmenování před ::. Příkladem mohou být standardní moduly File::Copy a File::Find. Jsou to dva různé moduly, první z nich poskytuje funkce pro kopírování souborů, druhý funkce pro vyhledávání souborů. Fyzicky se jedná o dva soubory -- Copy.pm a Find.pm, které leží v adresáři File. Znak :: je tedy překládán na znak oddělovače adresářů v daném operačním systému.

Tabulka symbolů balíku main obsahuje odkazy na tabulky symbolů všech balíků. Máme-li balík Balik, leží odkaz na tabulku symbolů %Balik:: v tabulce symbolů balíku main. Proto tabulka symbolů %Balik:: je to samé jako %main::Balik. Tabulka symbolů balíku main obsahuje i odkaz na sebe sama, proto je možné se dostat přes identifikátor %main::, %main::main:: atd.

V případě vhnízděných balíků leží v tabulce symbolů %main:: odkaz na tabulku symbolů, jejíž jméno se skládá pouze z části jména před ::. Při použití balíku File::Find je v tabulce symbolů %main:: pouze odkaz na tabulku %File:: a tato tabulka teprve obsahuje odkaz na tabulku %Find::.

package A;
package B;

for (keys %main::) {
	print "$_\n" if /::/;
}
# ve výpisu se objeví attributes::, UNIVERSAL::, DynaLoader::,
# A::, B:: a další

Autoloading

Jedním ze způsobů, jakým vznikají podprogramy. Tento mechanismus se může vyvolat v případě, že voláme podprogram, který v příslušném balíku není definován. Podmínkou je definice podprogramu s názvem AUTOLOAD v tom setjném balíku, kde hledání podprogramu selhalo. Funkce AUTOLOAD je potom volána se stejnými argumenty, jako byla volána původní funkce (ty jsou uloženy v proměnné @_), a do proměnné $AUTOLOAD, která je globální pro daný balík, se uloží jméno volaného podprogramu.

sub AUTOLOAD {
    # vypíšeme pouze varování
    warn "Pokus o volání neexistující funkce $AUTOLOAD\n
    s argumenty: ".join(', ', @_);
}

nejake_jmeno_funkce(1, 2, 3);

# vypíše:
# Pokus o volání neexistující funkce main::nejake_jmeno_funkce
# s argumenty: 1, 2, 3

Autoloading můžeme v některých případech používat i záměrně.

sub datum {
     my @udaje = localtime(time);
     $udaje[5] += 1900;
     my $datum = "$udaje[3]. $udaje[4]. $udaje[5]";
}

sub AUTOLOAD {
     $AUTOLOAD =~ s/.*:://;    # odstranění jména balíku
     $AUTOLOAD =~ s/log_//;    # odstranění prefixu
     print LOG &$AUTOLOAD(@_); # zápis do souboru pomocí
}                              # ovladače LOG, soubor už
                               # je otevřený
open LOG, '>log.txt';

log_datum();   # návratová hodnota funkce datum se 
               # zapíše do souboru

Tabulky symbolů a typegloby

Jména proměnných a odkazy na jejich obsahy jsou ukládány ve dvou typech paměťových struktur. Ty se nazývají tabulky symbolů a lexikální prostory. Zatímco lexikální prostory mohou obsahovat pouze jména a obsahy proměnných, tabulky symbolů navíc mohou obsahovat jména podprogramů, formátů a ovladačů. Jsou vztaženy vždy k určitému balíku, narozdíl od lexikálních prostory, které se vztahují k bloku a nemají s tabulkami symbolů nic společného.

Tabulka symbolů balíku je uložena v hashi, jehož jméno je shodné se jménem balíku a následuje znakem ::. Má tedy jméno %Balík::. Tabulka symbolů implicitně používaného balíku se jmenuje %main::, nebo také zkráceně %::. Tabulka symbolů balíku může obsahovat pouze identifikátory začínající písmenem nebo podtržítkem. Všechny ostatní identifikátory patří do balíku main (jsou to jména jako \$_, \$! apod.). Stejně tak nekvalifikované identifikátory STDIN, STDOUT, STDERR, ARGV, ARGVOUT, ENV, INC a SIG patří do balíku main. Tyto proměnné jsou jediné skutečně globální proměnné, protože je nikdy není třeba plně kvalifikovat. A to ani v případě, že použijeme use strict.

use strict;
$x = 1;    # chyba, není specifikováno jméno balíku
$_ = 1;    # v pořádku

Tyto proměnné nelze ani lokalizovat pomocí my.

Balíky není vhodné pojmenovávat m, s, y, q, qq, qr, qw, qx, tr. Budeme-li v tomto případě chtít plně kvalifikovat jméno proměnné v tomto balíku, bude to interpret považovat za volání stejnojmených operátorů.

package m;
sub f {print 'ahoj';}

package A;
m::f;
# volání funkce f z balíku m je považováno 
# za volání operátoru m//

Pro vytváření jmen existují určitá pravidla a platným jménem nemůže být např. řetězec *$&@%%)#@. Takovýto řetězec ale může být klíčem v hashi. Protože tabulka symbolů je hash, může i takovýto řetezec tvořit jeho klíč, tzn. může existovat např. proměnná tohoto jména. Toto jméno však nelze použít přímo, ale jako řetězec.

# jako klíč v hashi
%h = ( 'abc' => '222',
       '*$&@%%)#@' => '111' );
for (keys %h) { print " $_ => $h{$_}\n";}
# vytiskne
abc => 222
*$&@%%)#@ => 111 

# jako jméno proměnné v balíku main::
${'*$&@%%)#@'} = 1;
print ${'*$&@%%)#@'};

Typegloby

Jednotlivé hodnoty v hashi, který reprezentuje tabulku symbolů, jsou typegloby. Typeglob reprezentuje všechny objekty, které se jmenují stejně jako typeglob (*Balík::jmeno je všechno v balíku Balík, co se jmenuje jmeno). Jednotlivé typegloby je možné získat pomocí indentifikátorů (klíče v hashi reprezentujícím tabulku symbolů). Protože je tabulka symbolů hash, je možné vypsat všechny použité proměnné v balíku:

package Balik;

$a = 1; @a = (1,2,3);
$b = \$a;
%h = (1 => 'a');
sub f {print 1};

foreach my $symbol (keys %Balik::) {
    local *pom = $Balik::{$symbol};
    # v proměnné *pom je typeglob z tabulky symbolů balíku
    # Balik, který obsahuje všechno, co se jmenuje $symbol
    
    print "'$symbol' je skalár\n" if defined $pom;
    print "'$symbol' je pole\n" if @pom;
    print "'$symbol' je hash\n" if %pom;
    print "'$symbol' je podprogram\n" if defined &pom;
}

# vytiskne
'b' je skalár
'a' je skalár
'a' je pole
'f' je podprogram
'h' je hash

Je-li proměnná typeglob, můžeme zpětně zjistit, na jaký balík a jméno odkazuje. Pomocí *jméno{PACKAGE} zjistíme jméno balíku a pomocí *jméno{NAME} jméno záznamu v tabulce symbolů. Pomocí zápisu *jméno{CO} lze získat odkazy na jednotlivé proměnné, podprogramy, ovladače apod. (CO může nabývat hodnot, jaké vrací funce ref).

*Balík::jméno{SCALAR}    # to samé jako \$Balík::jméno
*Balík::jméno{ARRAY}     # to samé jako \@Balík::jméno
*Balík::jméno{CODE}      # to samé jako \&Balík::jméno
atd.

Vytváření aliasů

Pomocí typeglobů lze vytvářet pro jména aliasy. Přiřazením

*x = *y;

se docílí toho, že všechno, co se jmenuje y je dostupné rovněž pomocí identifikátoru x. Potom platí, že skaláry $y a $x, pole @y a @x, hashe %y a %x jsou totožné atd. Chceme-li vytvořit alias pouze pro některá jména, např. jména skalárních proměnných, použijeme následující konstrukce:

*x = \$y;

Potom skaláry \$y a \$x jsou totožné, ovšem pole @y a @x jsou rozdílná. Tímto způsobem pracuje modul Exporter -- v aktuálním balíku vytváří aliasy na proměnné, podprogramy apod. v jiném balíku.

Přiřazením odkazu na typeglob do typeglobu se stane to samé, jako při přiřazení typeglobu do typeglobu. Při použití odkazu na typeglob Perl provádí automatickou dereferenci. Při přiřazení řetězce do typeglobu dojde k přiřazení typeglobu, jehož jméno je zadáno jako řetězec. Všechny tři následující způsoby jsou tedy shodné a znamenaji tedy vytvoření aliasu x pro všechno, co se jmenuje y:

*x = *y;
*x = \*y;
*x = 'y';

Práce s odkazy bez dereference

Mechanismu vytváření aliasů lze použít pro zjednodušení a zrychlení práce s odkazy. Předáme-li jako argument funkci odkaz a chceme-li uvnitř funkce pracovat s odkazovaným obsahem bez dereference, je použití typeglobů možným řešením.

sub vypis_hash {
   local *symbol = shift;

   # modifikace hodnot hashe
   $symbol{'modifikováno'} = 'ano';
   
   # výpis hodnot předaného hashe
   for (keys %symbol) {
       print "$_ => $symbol{$_}\n";
   }
}

%hash = (1 => 'a', 2 => 'b', 3 => 'c');
vypis_hash(\%hash);

# vytiskne
modifikováno => ano
1 => a
2 => b
3 => c

Funkci by bylo také možné předat místo odkazu na hash typeglob *hash, odkaz na typeglob \*hash či řetězec 'hash'. Tělo funkce by zůstalo nezměněné a výsledek by byl stále stejný.

Vytváření alternativních pojmenování

Dalším využitím aliasů je vytvoření alternativních jmen pro jména funcí či proměnných. Existuje-li někde funkce s velmi dlouhým nebo špatně zapamatovatelným názvem, vytvoříme si pro ni alias a budeme ji volat pomocí tohoto jména.

Alternativní pojmenovávání speciálních proměnných realizuje modul English. Tento modul pouze vytváří aliasy pro existující speciální jména následujícím způsobem:

*ARG          = *_;
*CHILD_ERROR  = *?;
*PERL_VERSION = *^V;
*MATCH        = *&;
# atd.

Stejným způsobem bychom si mohli vytvořit česká pojmenování těchto proměnných a můžeme pak používat balík Czech.

Manipulace s ovladači a formáty

Ovladače a formáty jsou zvláštní datové typy, se kterými není možné provádět stejné operace jako s proměnnými. Není možné přiřazovat jim hodnoty nebo je lokalizovat. Při pokusu o provedení takovéto operace dojde k chybě.

# vytvoření ovladačů
open F, 'soubor1';
open G, 'soubor2';

# pokus o přiřazení a lokalizaci ovladače
F = G;
local F;

V obou případech překladač hlásí, že došlo k modifikaci konstatní položky.

sub projdi_soubory {
    local *F;           # lokalizujeme ovladač
    open F, shift;      # otevřeme soubor zadaný jako argument
  
    while (my $radek = <F>) {
       # pročítáme všechny řásky souboru
       chomp $radek;    
   
       # začíná-li řádek řetězcem 'Zdroj:',
       # jedná se o jméno dalšího souboru s textem;
       # zavoláme rekurzivně funkci na jeho zpracování
       if ($radek =~ /Zdroj:(\S+)/) {
           projdi_soubory($1);
           next; # aby se nezobrazilo jméno zdrojového souboru
       }
       # vytiskneme řádek
       print "$radek\n";
    }
}
# údaje jsou uloženy v souboru 'zdroj.txt', který obsahuje
#    nějaký text
#    další text
#    Zdroj:zdroj2.txt
#    zase text
# soubor 'zdroj2.txt' obsahuje
#    text z dalšího zdroje
#    ještě jeden řádek

projdi_soubory('zdroj.txt');

# vytiskne se
#    nějaký text
#    další text
#    text z dalšího zdroje
#    ještě jeden řádek
#    zase text

Následující příklad ukazuje funkci, která pracuje s ovladači souborů, ale nemusí být předem jasné, se kterými soubory budou tyto ovladače spojeny. Tyto ovladače jsou zadány funkci jako argument, proto je nutné je předat jako typegloby.

sub kopiruj {
     local *ZDROJ = shift;
     local *CIL = shift;
     while (<ZDROJ>) {
          print CIL;
     }
}
open F, "zdroj.txt";
open G, ">cil.txt";
kopiruj(*F, *G);

Vestavěné funkce jako např. open, které jako argument také požadují ovladač souboru, automaticky převádějí tento argument na typeglob. Není tedy třeba psát

open *F, 'soubor.txt';

Pomocí typeglobů a aliasů je také možné přesměrovávat vstupy nebo výstupy.

open *F, '>prvni.txt';
open *G, '>druhy.txt';
*G = *F;
print G "text";    # píšeme do souboru prvni.txt

Provádíme-li v nějakém skriptu výpis na obrazovku (na standardní výstup) a chceme-li, aby tento výstup byl přesměrován do souboru, není třeba u každého volání funkce print dopisovat jméno ovladače příslušného souboru, ale stačí udělat alias pro ovladač STDOUT.

# otevření soubotu pro uschování výstupu
open F, '>vystup.txt'
# vytvoření aliasu
*STDOUT = *F;
...
...
print $vystup;  # místo do STDOUT píšeme do F
...
}

Vytváření konstant

Pomocí typeglobů je také možné vytvářet konstanty určené jen pro čtení.

*PI = \3.141592654;

Od teď je možné používat proměnnou $PI, ale není možné měnit její hodnotu.

Rozsahy platnosti a tabulky symbolů, vymezování platnosti

Každý objekt (proměnná, podprogram, formát) v Perlu má svůj rozsah platnosti. Rozsah platnosti definuje oblast, kde je jméno tohoto objektu viditelné, a způsob, jakým je možné k takovému objektu přistupovat. Rozlišujeme dva základní rozsahy platnosti -- globální platnost a platnost omezená. Platnost omezená se dále dělí na platnost lexikální (lexikální proto, že se vztahuje vždy k určité části kódu, textu), lexikální platnost globálních jmen a platnost dynamicky vymezená (vytváří lokální hodnoty globálních proměnných).

Globální deklarace

Globálně deklarované objekty jsou vždy globální v rámci balíku, kde jsou deklarovány. Leží v tabulce symbolů a jsou tedy přístupné odkudkoliv. Je to proto, že všechny tabulky symbolů jsou obsaženy v tabulce balíku main. Platí pravidlo, že deklarace podprogramů, formátů a ovladače jsou vždy globální, náleží vždy do tabulky symbolů. Globální deklarace jsou vždy interpretem zaznamenány v době překladu a jsou potom viditelné v rámci celého balíku. Nemusejí tedy být nutně na začátku. Často se vyskytují i na konci balíku či souboru nebo v jiném souboru, ze kterého jsou vtaženy. Mohou se ale vyskytovat kdekoliv.

U podprogramů je vhodné provést dopřednou deklaraci před jejich prvním použitím, aby s nimi bylo možné pracovat jako s vestavěnými funkcemi. Od místa deklarace totiž není nutné používat kulaté závorky kolem seznamu argumentů.

soucet (1,2,3);   # závorky musejí být použity

sub soucet;       # deklarace funkce

soucet 2,3,4;     # závorky nemusejí být použity

sub soucet {      # definice funkce
	$soucet = 0;
	$soucet += $_ for @_;
	print $soucet;
}

Deklarace formátů je možné uvést kdekoliv, při použití ovladače spojeného s tímto formátem bude vždy viditelný.

Proměnné s omezenou platností

Pomocí deklarace my je možné zajistit omezenou platnost pro jména i hodnoty proměnných, pomocí local omezenou platnost pro hodnoty proměnných a pomocí our alias s omezenou platností pro globální proměnné.

Vymezení platnosti pomocí my se nazývá lexikální. Je to proto, že tyto proměnné souvisejí s úsekem kódu (textu), v němž byly deklarovány. Pomocí lexikálního vymezení se jména a hodnoty proměnných neukládají do tabulky symbolů, ale do zvláštních paměťových struktur. Tyto struktury jsou vztaženy ke svému vlastnímu prostoru a nejsou proto viditelné zvenčí. Je vhodné, aby se lexikálně vymezené proměnné vztahovaly co k nejmenšímu rozsahu.

Pomocí lexikálního vymezení globálních proměnných pomocí our se pracuje s golbálními proměnnými dostupnými odkudkoliv tím způsobem, jako by to byly proměnné s lexikálním rozsahem platnosti.

Lexikálně vymezovat se dají pouze proměnné jako celek, ne tedy například jeden prvek pole, hodnotu hashe, část pole. Není možné takto pracovat s objekty, které vždy náležejí do tabulky symbolů, tzn. typegloby, podprogramy, formáty, ovladači.

Pomocí dynamického vymezení platnosti pomocí localje možné pracovat s globálními proměnnými tak, že jejich hodnoty jsou ukryty a po skončení platnosti takové proměnné jsou tyto hodnoty obnoveny zpět.Dynamicky vymezovat je možné nejenom celé proměnné, ale i jejich části - prvky pole, hodnoty hashe či části pole. Je rovněž možné lokalizovat celé typegloby.

Chceme-li najednou omezit platnost několika proměnných, je třeba tento seznam uzavřít do kulatých závorek.

my ($x, $y, $z);   # lokalizuje všechny tři proměnné
my $x, $y, $z;     # to samé jako my $x; $y, $z;

Použití my a local má za následek to, že hodnota takto lokalizované proměnné má nastavenu hodnotu na nedefinovanou. Hodnotu je možné nastavit později nebo v tom samém výrazu pouhým přiřazením.

my $x = 1;

Použití our hodnotu globální proměnné ponechává. Není vytvářena nová proměnná, ale pracuje se s existující proměnnou. Proto je zbytečné provádět tuto deklaraci na proměnnou stejného jména ve vnořených blocích.

package A;
our $x = 1;   # nastavení hodnoty $A::x
{
  our $x = 2; # opět nastavení hodnoty $A::x
}
# tady má proměnná $x hodnotu 2

Lexikální platnost proměnných

Nejčastější způsob vymezení platnosti. Lexikálně vymezené proměnné jsou uchovávány ve zcela odlišných strukturách. Tyto struktury se nazývají scratchpad a jsou to pole, jejichž každý prvek obsahuje jedno jméno proměnné. Je tomu tak i v případě, že proměnné mají za prefixem stejná jména -- např. $x, @x, %x. Druhou částí je další pole, které obsahuje odkazy na hodnoty proměných na odpovídajících indexech v polích se jmény proměnných. Tato pole jsou předávána do podprogramů nebo do vnořených bloků. Je-li podprogram volán rekurzívně, je mu toto pole předáváno několikrát.

Platnost lexikálně vymezené proměnné je do konce bloku, ve kterém byla deklarována. Je rovněž viditelná v blocích či tělech podprogramů, které jsou vnořené.

{ my $x = 1;          # lexikálně vymezená proměnná
  print $x;           # vytiskne 1
  { print $x;         # vytiskne 1
    sub f {print $x;}
    f;                # vytiskne 1
  }
  f;                  # vytiskne 1
}                     # konec platnosti proměnné $x 
f;                    # vytiskne 1
print $x;             # globální proměnná $x není inicializována


$x = 1;        # globální proměnná
{ my $x = 2;   # lexikální proměnná
  print $x;    # vytiskne 2
  { my $x = 3; # další lexikální proměnná
    print $x;  # vytiskne 3
    sub f {print $x;}
  }
}
f;             # vytiskne 3
print $x;      # tisk hodnoty globáoní proměnné,
               # vytiskne se 1

Existuje-li lexikálně vymezená proměnná se stejným jménem jako globální proměnná a cheme-li pracovat s globální proměnnou, musíme provést plnou specifikaci jména proměnné (to znamená jméno proměnné včetně jména balíku).

Dynamické vymezování platnosti

Oproti tomu dynamické vymezení proměnné pomocí local nevytváří novou proměnnou, ale pouze uschová její původní hodnotu. Po skončení platnosti této proměnné je původní hodnota obnovena zpět.

$x = 1;
{
  local $x;     # uschování původní hodnoty
  $x = 'abc';
  print $x;     # vytiskne 'abc';
}               # konec platnosti, obnovení 
                # původní hodnoty
print $x;       # vytiskne '1'

Vlastnosti, že původní hodnota proměnné je po skončení platnosti obnovena zpět, je vhodné používat local pro dočasné změny hodnot vestavěných proměnných. Pro specifický účel je možné jejich předdefinovanou hodnotu změnit, poté je jejich hodnota vrácena zpět na implicitní. Může se to týkat proměnných $/, $,, $%, pole @ARGV, ovladače STDOUT apod.

Díky tomu, že local nevytváří novou proměnnou, je možné uschovat hodnotu i části datové struktury -- např. prvek pole nebo hashe nebo část pole.

@a = (1,2,3);
print join ', ', @a;   # vytiskne '1, 2, 3'
{
  local $a[1];         # uschování hodnoty druhého
                       # prvku pole
  $a[2] = 'xxx';
  print join ', ', @a; # vytiskne '1, xxx, 3'
}                      # obnovení hodnoty druhého
                       # prvku pole
print join ', ', @a;   # vytiskne '1, 2, 3'

Lexikální vymezování jmen globálních proměnných

Pomocí our je možné přistupovat ke globálním proměnným tak, jako by to byly proměnné lexikálně vymezené. Rozdíl je v tom, že jejich jména ani obsahy nejsou skryty před okolím. Tato deklarace proměnných je vhodná pro práci s globálními proměnnými při zapnutém striktím režimu. V tomto režimu můžeme buď jména plně specifikovat (uvádět je včetně jména balíku) nebo pomocí our říci, že patří do aktuálního balíku. Pak už není nutné jejich jména plně specifikovat a je možné s nimi pracovat tak, jako by to byly lexikálně vymezené proměnné.

Deklarace our překrývá platnost případných proměnných stejného jména, které jsou pro daný prostor lexikálně vymezeny.

$x = 1;
print $x;    # vytiskne 1

my $x = 2;
print $x;    # vytiskne 2

our $x;
print $x;    # vytiskne 1

Vyhledávání jmen proměnných

Při vyhledávání jména proměnné se nejprve zjišťuje, zda v aktuálním bloku neexistuje proměnná takového jména lexikálně vymezená pomocí my nebo our. Jestliže ne, prohledávají se bloky nadřazené tak dlouho, dokud takové jméno není nalezeno nebo dokud není dosaženo bloku na nevyšší úrovni.Nebylo-li jméno nalezeno v žádném z procházených bloků, musí se nacházet v tabulce symbolů. V té se hledá i v případě, že je jméno proměnné plně kvalifikováno.

Proces vyhledávání postupuje po úrovních vzhůru vždy pouze po hranice souboru. Soubor je totiž největší možná lexikální jednotka. Lexikální proměnná tak nikdy není viditelná za hranicemi souboru, v němž byla deklarována. V jednom okamžiku se můžeme nacházet v několika lexikálních prostorech. Pokaždé je na nejvyšší úrovni soubor a v něm mohou existovat vnořené lexikální prostory díky zanořeným blokům. Prostor balíku je aktivní vždy právě jeden. Je to proto, že neexistují balíky vnořené do sebe. Proměnná existuje vždy pouze v jednom prostoru, nemůže existovat zároveň v lexikálním prostoru a zároveň v prostoru balíku.

my $lexikalni = 1;
$globalni = 2;             # $main::globalni

package A;

print $lexikalni;          # vytiskne '1'
print $globalni;           # $A::globalni, nevytiskne nic

{ print $lexikalni;        # vytiskne '1'
  my $lexikalni = 3;       # překrývá se platnost $lexikalni
  print $lexikalni;        # vytiskne '3'
  
  eval 'print $lexikalni'; # vytiskne '3'
  eval 'print $globalni';  # $A::globalni, nevytiskne nic
  eval 'my $globalni = 4;  # nová lekikálně vymezená proměnná
        print $globalni';  # vytiskne '4'
}
print $lexikalni;          # vytiskne '1';

Je-li použit striktní režim, je vyžadováno, aby všechny proměnné (kromě speciálních proměnných typu $_ apod.) byly buď plně specifikovaány, tzn. včetně jména balíku, nebo byly lexikálně vymezené pomocí my nebo our. V okamžiku, kdy je dokončeno prohledávání nadřazených bloků na existenci proměnné a jméno nebylo nalezeno, dojde k chybě v případě, že jméno nebylo importováno z jiného modulu.

use strict;
use Modul '$importovana';

print $importovana;   # v pořádku
$neimportovana = 1;   # CHYBA

Inicializace a opuštění balíku

Při prvním vstupu do určitého balíku hledá interpret funkce, které se jmenují BEGIN. Jedná se o obyčejné podprogramy, jejichž definice se v programu mohou objevit. Každá z těchto funkcí (někdy se užívá označení blok) je potom provedena už v době překladu ještě před tím, než je program spuštěn.

Po skončení fáze kompilace jsou provedeny bloky CHECK. Jako první jsou při spuštění programu provedeny všechny bloky INIT.

Po úplném skončení běhu programu jsou prováděny bloky END. Ty jsou spuštěny i tehdy, ukončí-li se program předčasně vyvoláním chyby (např. pomocí die). Proměnná $? obsahuje hodnotu, s jakou bude program ukončen. Bloky END jsou přeskočeny v případě, přepíšeme-li běžící proces voláním funkce exec nebo došlo-li k nezachycené fatální chybě.

Bloky BEGIN a END jsou někdy nazývány jako konstruktory a destruktory balíků.

Každý z těchto bloků je definován jako obyčejný podprogram -- tzn. klíčové slovo sub, jméno a tělo podprogramu. Pro definici takto pojmenovaných funkcí není třeba slovo sub uvádět (podobně jako pro funkci AUTOLOAD).

Je-li jednotlivých bloků v balíku více, nebo je-li jich více v důsledku vtažení z jiného souboru, pořadí jejich provádění je následující. Bloky BEGIN jsou provedeny jako první a to v tom pořadí, v jakém jsou definovány. Potom jsou provedeny bloky CHECK, ale v opačném pořadí, než byla jejich definice. Pak následují bloky INIT a jsou provedeny také v pořadí jejich definice. Bloky END jsou prováděny jako poslední a to v pořadí opačném.

BEGIN {print "BEGIN 1\n"};
END {print "END 1\n";}
BEGIN {print "BEGIN 2\n";}
INIT {print "INIT 1\n";}
CHECK {print "CHECK 1\n";}
BEGIN {print "BEGIN 3\n";}
END {print "END 2\n";}
CHECK {print "CHECK 2\n";}
INIT {print "INIT 2\n";}
print "Hlavní program\n";

# vytiskne
BEGIN 1
BEGIN 2
BEGIN 3
CHECK 2
CHECK 1
INIT 1
INIT 2
Hlavní program
END 2
END 1
© 2004, František Dařena

Valid XHTML 1.0! Valid CSS!