Moduly

Modul je souhrn funkcí, datových struktur a kódu, který je možné opětovně používat v různých programech. Souhrn prostředků, které modul poskytuje je většinou ucelený a souvisí s určitou problematikou. Existují moduly pro práci se soubory, pro práci s databází, webovým rozhraním, pro síťovou komunikaci apod. Každý modul má zajištěn svůj vlastní prostor pro ukládání jmen (nenarušuje své okolí více, než potřebujeme) a má definováno rozhraní pro okolní svět (soubor prostředků, které svému okolí poskytuje).

V Perlu platí pravidlo, že modul je balík, který je uložen ve stejnojmenném souboru s příponou .pm.

Dodržování přípony souboru je nutné v případě, že modul zavádíme pomocí use. Tato funkce slouží pouze k zavádění modulů, narozdíl od funkce require, pomocí které lze do programu načíst soubor s libovolnou příponou. Není-li přípona uvedena a není-li jméno souboru zadáno jako řetězec v uvozovkách nebo apostrofech, použije se také standardní .pm.

Konvence při psaní jmen modůlů, pragmatické moduly, standarní moduly, CPAN.

Zavedení modulu

Aby bylo možné modul použít, je třeba ho nejprve zavést. K tomu slouží vestavěné funkce use a require.

require zahrne soubor pomocí mechanismu do SOUBOR. Jako argument očekává jméno souboru, který má být načten. Je-li vynechána přípona souboru a nepoužijeme-li uvozovky či apostrofy k ohraničení jména souboru, automaticky se doplní přípona .pm. Při zavádění pomocí use se jméno modulu píše bez uvozovek a bez přípony. Přípona .pm je doplněna automaticky.

Funkce use i require je možné použít kdekoliv v programu, ne tedy nutně na začátku. Rozdíl je v platnosti provedených akcí.

print $CGI::VERSION;  # vytiskne '2.81'
use CGI;

print $CGI::VERSION;  # neinicializovaná proměnná
require CGI;
print $CGI::VERSION;  # vytiskne '2.81'

Vtažení modulu pomocí use probíhá narozdíl od require v době překladu. Použijeme-li tedy use, máme zajištěno, že všechny moduly, které hodláme použít, jsou syntakticky správně.

Při prvním zavedení modulu se provede záznam do hashe %INC. Klíči jsou jména modulů a hodnotami jsou cesty k těmto souborům. Při každém dalším pokusu se nejprve podle tohoto hashe zjistí, zda už zavedené modulu takového neproběhlo. V případě že ano, se další zavedeé neprovádí.

Oba způsoby zavedení očekávají, že zaváděný soubor vrátí pravdivou hodnotu, která indikuje, že vše proběhlo v pořádku. Proto by posledním výrazem v příslušném souboru měl být pravdivý výraz. Často se jako poslední příkaz používá return 1; nebo pouze 1;. V případě neúspěchu je vrácena nulová hodnota.

Kde se moduly hledají?

Při zavádění modulů se hledá v adresářích, jejichž jména jsou uložena v globálním poli @INC nebo v proměnné prostředí PERL5LIB.

Nastavit adresáře, kde se mají moduly hledat, lze přímo modifikací pole @INC. V případě použití use, kdy je modul zaváděn v době překladu, je nutné tuto modifikaci provést v bloku BEGIN. Dalšími možnostmi jsou natavení proměnné prostředí PERL5LIB, použití pragmatického modulu use lib nebo spuštění Perlu s přepínačem -I.

# modifikací pole @INC
unshift @INC, '/home/darena/lib';

# použití pragmatického modulu lib
use lib '/home/darena/lib';

# spuštění Perlu s přepínačem -I
perl -I/home/darena/lib skript.pl

Pokud jméno zaváděného modulu obsahuje znak ::, je tento znak nahrazen oddělovačem adresářů. Modul File::Find bude tedy hledán kromě prohledávaných adresářů vždy v případných podadresářích File (hledá se soubor Find.pm.

Vytváření modulů

Modul je soubor obsahující to samé, co ostatní programy napsané v Perlu. To znamená, že obsahuje proměnné, podprogramy, různé deklarace, příkazy atd. Navíc je třeba interpretu řící, že se jedná o modul a specifikovat rozhraní pro své okolí.

To, že se jedná o modul, je dáno umístěním v souboru s příponou .pm a definováním stejnojmenného balíku v tomto souboru.

Rozhraní je možné poskytnout dvěma způsoby. Prvním způsobem je definovat množinu symbolů, které je možné exportovat. To znamená, že tam, kde budeme chtít modul použít, řekneme, že některá jména mají být importována do aktuálního prostoru jmen a bude je tam možné používat bez plné specifikace (bez jména balíku). Druhým způsobem je napsat modul objektově orientovanýcm způsobem a komunikovat pouze prostřednictvím metod objektů. Oba způsoby je možné kombinovat - modul bude napsán objektově orientovaným způsobem a zárověň bude umožňovat export některých symbolů.

package Datum;

# verze modulu
$VERSION = 1.0;

# použití modulu Exporter pro definici rozhraní
use Exporter;
@ISA = ('Exporter');
@EXPORT = ('aktualni_datum');
@EXPORT_OK = ('aktualni_datum', 'aktualni_datum_slovne', '%mesice');
%EXPORT_TAGS = (vsechno => [qw(aktualni_datum
                               aktualni_datum_slovne
                               %mesice)]);
                              
# definice podprogramů
sub aktualni_datum {
     my @udaje = localtime(time);
     $udaje[5] += 1900;
     return "$udaje[3]. $udaje[4]. $udaje[5]";
}
sub aktualni_datum_slovne {
     my @udaje = localtime(time);
     $udaje[5] += 1900;
     return "$udaje[3]. $mesice{$udaje[4]} $udaje[5]";
}

# vlastní kód modulu
%mesice = (1 => 'Leden', 2 => 'Únor', 3 => 'Březen',
           4 => 'Duben', 5 => 'Květen', 6 => 'Červen',
           7 => 'Červenec', 8 => 'Srpen', 9 => 'Září',
           10 => 'Říjen', 11 => 'Listopad', 
           12 => 'Prosinec');

# indikace úspěšného zavedení modulu
1;

Export a import symbolů, modul Exporter

Chceme-li provést export jmen do jiného balíku, je třeba v případě, že nechceme použít objektově orientovaného přístupu, použít standardní modul Exporter. Ten dělá to, že námi specifikovaná jména exportuje do tabulky symbolů programu, kde chceme modul použít. Tento mechanismus probíhá s využitím předem daných datových struktur. Jedná se o pole @EXPORT, @EXPORT_OK a @EXPORT_TAGS definovaná ve vytvářeném modulu. Je nutné, aby se pole jmenovala právě takto, protože s těmito jmény modul Exporter pracuje.

Při volání funkce use se volá funkce import z modulu, který chceme zavést. Použitím konstrukce @ISA = ('Exporter') se zajistí zdědění metod modulu Exporter a tedy i funkce import. Tato funkce zajistí vytvoření aliasů pro jména, která chceme importovat. Postup při volání use je ekvivalentní zápisu

BEGIN {
    require Modul;
    import Modul <SEZNAM SYMBOLŮ>
    # nebo Modul->import(<SEZNAM SYMBOLŮ>)
}

Volání v bloku BEGIN zajistí to, že symboly se budou importovat ještě před prováděním ostatního kódu, stejně jako v případě use. Použitím kdekoliv jinde v programu by symbol byl viditelný až od okamžiku, kdy byla funkce require volána. Voláním use se zajistí to, že všechny importované symboly budou viditelné kdekoliv od začátku programu.

Pole @EXPORT v modulu slouží k definování jmen, která budou exportována do tabulky symbolů programu, který modul bude chtít použít. Stane se tak v případě, že je modul použit způsobem use Modul; bez specifikace importovaných jmen. Pro všechna jména z tohoto pole rovněž existuje synonymum :DEFAULT.

Jména exportovaných symbolů jsou hodnotami výrazů uvedených jako prvky tohoto pole. Nejčastěji však bývají zadány jako řetězce (v kulatých závorkách, ohraničené uvozovkami či apostrofy, nebo pomocí operátoru qw), ale je možný i jiný zápis.

@EXPORT = ('aktualni_datum');
@EXPORT = qw(aktualni_datum);

$jmeno = 'aktualni_datum';
@EXPORT = ($jmeno);

sub jmeno {return 'aktualni_datum';}
@EXPORT = (jmeno);

Jména uvedená v tomto poli budou exportována automaticky v případě, že explicitně neuvedeme, jaká jména chceme importovat. Znamená to, že když použijeme modul Datum pouze pomocí use Datum, bude importováno pouze jméno funkce aktualni_datum.

Pole @EXPORT_OK slouží k definování jmen, která je možné importovat na požádání. Tato jména jsou specifikována v okamžiku použití modulu pomocí use.

use Modul 'jmeno1', 'jmeno2';

Pomocí hashe %EXPORT_TAGS je možné jména exportovat hromadně. Klíče tohoto hashe pojmenovávají skupiny exportovaných jmen a hodnoty tvoří odkazy na seznamy s těmito jmény. Jména uvedená v jednotlivých seznamech musejí být také uvedena v jednom z polí @EXPORT nebo @EXPORT_OK. V opačném případě vznikne chyba.

Jméno, které není uvedeno v žádné z těchto struktur, není možné jinde použít bez plné specifikace (tzn. včetně jména balíku).

Pomocí funkcí export_tags a export_ok_tags opět zděděných z modulu Exporter je možné přidávat symboly do polí @EXPORT a @EXPORT_OK. Tyto symboly jsou brány ze skupin nadefinovaných v hashi %EXPORT_TAGS.

%EXPORT_TAGS = (a => [qw($x $y)],
                b => [qw(@x @y)]);
Modul->export_tags('a');
# do pole @EXPORT přidá symboly $x a $y
Modul->export_ok_tags('b');
# do pole @EXPORT_OK přidá symboly @x a @y

V případě, že exportujeme typeglob, můžeme v souboru, kam modul zavedeme, používat všechna jména, která se jmenují stejně jako typeglob.

package Modul;

use Exporter;
@ISA = qw(Exporter);

@EXPORT = ('*jmeno');

$jmeno = 1;
@jmeno = qw(a b c);
%jmeno = (1 => 'a', 2 => 'b');
sub jmeno {print 'podprogram jmeno';}
open 'jmeno', '>jmeno.txt';
format jmeno =
@>>>>
$jmeno
.

Použijeme-li někde tento modul a importujeme-li symbol jmeno, můžeme bez plné specifikace jména používat všechno, co se jmenuje jmeno.

use Modul;

print $jmeno;                 # vytiskne '1'
print join ', ', @jmeno;      # vytiskne 'a, b, c'
print join ', ', keys %jmeno; # vytiskne '1, 2'
jmeno;                        # vytiskne 'podprogram jmeno'
write 'jmeno';                # do souboru n.txt se 
                              # zapíše '    1'

Exportováním jmen se zasahuje do jmenného prostoru toho balíku, kde provádíme import. Proto je vhodné místo pole @EXPORT raději používat pole @EXPORT_OK. V tomto případě si ten, kdo chce modul použít, musí specifikovat seznam jmen, která chce importovat, a sám si ohlídá to, že se mu nebudou plést dohromady stejnojmenné objekty z obou prostorů jmen.

V Perlu neexistuje způsob, jak říci, že některé objekty (proměnné, podprogramy) jsou privátní a nelze je použít mimo jejich prostor jmen. Ani modul Exporter tot zajistit neumí. Tím, že některá jména neexportujeme, nezakazujeme jejich použití, ale pouze je nemůžeme používat bez jejich plné specifikace. Export jmen je totiž v podstatě proces vytváření aliasů v aktuálním balíku pro jména v jiném balíku. Nemožnost získání hodnot objektů mimo modul je možné tak, že objekty tohoto jména neumístíme do tabulky symbolů. U proměnných se toho dosáhne lexikálním vymezením platnosti pomocí my. U podprogramů toho lze docílit tak, že se do lexikálně vymezené proměnné uloží odkaz na podprogram.

package Modul;

use Exporter;
@ISA = ('Exporter');

@EXPORT = qw($funkce $promenna);

my $funkce = sub {print 'funkce'};
my $promenna = '123';

&$funkce;               # vytiskne 'funkce'
print $promenna;        # vytiskne '123'

# použijeme Modul
use Modul;              # exportují se jména $funkce a $promenna

&$funkce;               # volání nedefinované funkce
&{$Modul::funkce}       # to samé
print $promenna;        # použití nedefinované hodnoty
print $Modul::promenna; # to samé

Importování jmen podle vzorů

Jména, která se mají importovat, je možné popsat nejen výčtem, ale i jejich specifikací pomocí vzoru, jaký známe z regulárních výrazů. Tyto vzory se očekávaně uvádějí mezi dvojici lomítek.

Pomocí následujícího příkladu se provede s modulu Fcntl import jmen, která začínají na S_. Jedná se o jména, která se mohou použít např. pro nastavení oprávnění k souboru pomocí funkce chmod (podobného efektu se dosáhne použitím skupiny :mode).

use Fcntl qw(/^S_/);

Zákaz importování symbolů

Vyskytne-li se v seznamu importovaných jmen znak ! před importovaným jménem, znamená to, že toto jméno má být ze seznamu jmen vyjmuto. To znamená, že je nezbytné, aby toto jméno bylo mezi seznamem importovaných jmen.

Vše bude ukázáno v následujícím příkladu. Máme modul Modul.pm a z něho exportujeme symboly $x, $y a $z.

package Modul;

use Exporter;
@ISA = qw(Exporter);

@EXPORT = qw($x $y $z);

Při použití use Modul se importují všechna jména uvedená v poli @EXPORT. Chceme-li ale importovat pouze jména $x a $y, použijeme modul následujícím způsobem.

use Modul qw($x $y $z !$z);

Tím, že jsme před $z uvedli znak !, zakázaji jsme import tohoto jména. Kdybychom napsali pouze

use Modul qw($x $y !$z);

došlo by k chybě, protože symbol $z není mezi importovanými symboly.

V případě, že v seznamu importovaných jmen budou všechna jména začínat znakem !, provede se to, že budou importována všechna jména z @EXPORT a z nich budou vyjmuta ta jména, uvedená v seznamu. Je to proto, že v tomto případě se implicitně na začátek seznamu doplní symbol :DEFAULT, což je synonymum pro všechna jména uvedená v poli @EXPORT.

use Modul qw(!$y !$z);

V tomto případě se provede import všech jmen uvedených v poli @EXPORT a z nich se potom vyjmou symboly $y a $z. Je to to samé, jako bychom napsali

use Modul qw(:DEFAULT !$y !$z);

V případě, že seznam importovaných symbolů nezačíná znakem !, musíme v případě potřeby symbol :DEFAULT uvádět explicitně.

use Modul qw($x $y !$z);     # špatně
use Modul qw(:DEFAULT !$z);  # správně

Znak ! pro zákaz importování některých jmen je možné kombinovat se všemi ostatními symboly, které se mohou vyskytnout v seznamu importovaných jmen. To znamená, že je možné napsat:

use Modul qw(!symbol !:skupina !/vzor/);

Zákaz exportování symbolů

Umístěním některých symbolů do pole @EXPORT_FAIL v modulu, zabráníme jeho importu.

# definice modulu
package Modul;

use Exporter;
@ISA = ('Exporter');

@EXPORT = qw($x $y $z);
@EXPORT_OK = qw(f g);
%EXPORT_TAGS = (vsechno => [qw($x $y $z f g)]);
@EXPORT_FAIL = qw($x);      # zákaz exportu $x

# použití modulu
use Modul qw($y $z f g);    # v pořádku
use Modul qw(:vsechno !$x); # v pořádku
use Modul;                  # chyba
use Modul qw(:vsechno);     # chyba
use Modul qw ($x $y $z);    # chyba

Verze modulů

Modul Exporter umí zajistit, aby bylo možné používat modul pouze od určité verze. K určení verze modulu slouží proměnná $Modul::VERSION. Té se přiřadí hodnota udávající aktuální verzi. Použitím use s číslem udávajícím minimální verzi modulu, která je třeba, se zajistí to, že moduly s nižší verzí není možné použít.

# definice modulu
package Modul;
$VERSION = '1.89';  # definování verze modulu

# použití modulu
use Modul 1.7:      # použití modulu s minimální
                    # verzí 1.7

Vlastní import jmen

Chceme-li, aby se jména importovala jiným způsobem, než jakým to provádí modul Exporter, nebo aby byly prováděny některé další činnosti, stačí ve vytvářeném modulu nadefinovat funkci s názvem import. Tato funkce je volána pokaždé při použití modulu pomocí use. Kdybychom chtěli, aby se funkce chovala standardně a ještě vykonávala něco navíc, použijeme funkci export_to_level, která je rovněž zděděna z modulu Exporter. Jako první argument je očekáváno číslo, které udává počet úrovní směrem nahoru, kam se mají jména exportovat. Má-li se provést export do programu, který modul používá, bude to o jednu úrověň. Dalším argumentem je seznam symbolů, které se mají exportovat.

sub import {
   # nějaké akce
   # ...
   Modul->export_to_level(1, @_);
}
© 2004, František Dařena

Valid XHTML 1.0! Valid CSS!