Пакети, модули, класове, обекти (1/2)
- Perl има средства за обектно-ориентирано програмиране —
класове и обекти; те са разпределени в пакети и модули.
- Пакет — логическа част от програмата,
която има собствена секция от символната таблица —
namespace — т.е. „собствени“ функции и
„глобални“ променливи;
- Модул — файл, съдържащ дефиниции на функции,
променливи, пакети; нещо като библиотека, която можете да
използвате, като включите в програмата си файла;
Пакети, модули, класове, обекти (2/2)
- Клас — тип данни с някакви свойства,
състояние, върху които можете да извършвате някакви
„по-особени“ операции;
- Обект — променлива, която има за тип
някакъв клас — върху нея можете да извършвате операциите
(методи) и да използвате състоянието й (свойства,
член-променливи)
Пакети
- Променливите и функциите не се разполагат директно в символната
таблица, а в нейни секции (namespaces)
- Име на секцията задаваме с директивата
package
- Можем да зададем йерархия с използване на разделител
„::“
- По подразбиране работим (дефинираме) в пакета
main
- В един файл можем да дефинираме променливи и функции от много
различни пакети в произволен ред
Пример: пакети
- @types = qw/animals plants/;
package Animal;
@types = qw/verterbrae invertebrae fungi/;
package Animal::Cat;
@types = qw/persian alley/;
package Animal::Dog;
@types = qw/dachshund poodle sausage/;
package main;
print "My types: @types\n";
print "Animals: @Animal::types\n";
print "Cats: @Animal::Cat::types\n";
print "Dogs: @Animal::Dog::types\n";
Модули: require (1/2)
- Модул (библиотека): функции и променливи,
дефинирани в един файл, който включваме в нашата програма
- За да включим модула, използваме
- require име::на::модул;
- Във файла, който е указан като параметър на require, може да
има повече от един пакет; включваме наведнъж всичко, което ни
трябва.
- След инсталация Perl има дефинирани по подразбиране пътища за
търсене на модули (library search path). Те са описани в
променливата @INC, разположена в main пакета
(@main::INC)
Модули: require (2/2)
- При зареждане на модул Perl го търси разположен в поддървото на
всяка една от директориите от @INC, като „::“
(двоеточията) в името на модула (напр. My::Module) се
интерпретират като влизане навътре в директорийната структура и
зареждане на файла от там (съответно: My/Module.pm)
- require проверява дали сме задали низ или
просто bareword (поредица от символи без апострофи или кавички).
Ако параметър на require e bareword, то накрая се добавя
„.pm“ и заменя всички „::“ с '/' (под
Windows с '\'), след което зарежда файла.
Модули: import, use
- Включването дефинира всички функции и променливи в namespace-а
(пакета), в който те са дефинирани в модула
- import — можем да „внесем“
функции и променливи в текущия ни namespace, така че да ги
използваме без пълното име на пакета им
- use — по-лесен начин за изпълняване на
require и import наведнъж.
Използва се най-често.
Пример: модули
- require 'File/Temp.pm';
$wrkdir = File::Temp::tempdir();
- require File::Temp;
import File::Temp qw/tempdir tempfile/;
$wrkdir = tempdir();
- use File::Temp qw/tempdir tempfile/;
$wrkdir = tempdir();
- use Carp;
croak("I thought I thaw a tweety bird!");
Процедурни модули
- Процедурни са повечето по-стари Perl модули, но са още често
срещани.
- Разчитат на експортиране на символи (променливи, подпрограми)
в символната таблицата на пакетите, които ги използват
- Има стандартен начин това да се случва, макар и да не е
задължително при писане на модули.
Конструктори и деструктори
- Във всеки процедурен модул могат да се дефинират подпрограми
със специални имена, които играят ролята на конструктори и
деструктори и се изпълняват както следва:
- BEGIN — изпълнява се възможно най-скоро
@ compile time (FIFO)
- CHECK — изпълнява се в края на compile
time (LIFO)
- INIT — изпълнява се в самото начало на
run-time (FIFO), преди самия код
- END — изпълнява се възможно най-късно,
точно преди интерпретатора да приключи работа (LIFO)
Експортиране на функционалност (1/2)
- Променливите и подпрограмите разположени в даден модул са
„затворени“ в него по подразбиране и достъп до тях
може да се осъществява само с пълните имена.
- С помоща на основния модул Exporter се реализира експортиране
на подпрограми и променливи в namespace-а на потребителската
програма. След експортиране те стават достъпни, все едно са
дефинирани в текущия namespace.
- Perl автоматично извиква import при употреба на use и модулът
Exporter реализира функционалността на този метод.
Експортиране на функционалност (2/2)
- В няколко специални списъка се описват като скаларни стрингове
имената на експортираните променливи и функции:
- @EXPORT — експортирани по подразбиране
- @EXPORT_OK — експортирани, ако бъде указано от
потребителя на модула
- %EXPORT_TAGS — тук попадат описанията на групи
от функции/променливи, които да бъдат експортирани,
ако потребителят на модула укаже. Това става, като
при use се използва следният синтаксис:
- use Module qw/:tagname/;
Структура на процедурен модул, използващ Exporter
- package Some::Module; # приема се, че това е Some/Module.pm
use strict;
use warnings;
use Exporter ();
our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS);
$VERSION = 1.00;
@ISA = qw/Exporter/;
@EXPORT = qw/&func1 &func2 &func4/;
%EXPORT_TAGS = ( 'a' => [ &func1 &func2 ], 'b' => [ &func4 ] );
@EXPORT_OK = qw/$var1 $var2 @arroo %hashit &func5/;
Структура на процедурен модул, използващ Exporter
- our ($var1, $var2, @arroo) = qw/1 5 and more/;
our %hashit;
# Non-exported package globals go here:
our @more;
our $stuff;
# File-private lexicals go here:
my $priv_filename = 'you-ll-never-know.txt';
my %secret_hash = ();
# Here's a file-private function as a closure:
my $priv_func = sub {
# stuff goes here
}
Структура на процедурен модул, използващ Exporter
- sub func1; # no prototype
sub func2(); # proto'd to void
sub func3($ $); # proto'd to 2 scalars
# This one isn't exported, yet it could be
# called as Some::Module::func4()!
sub func4(\%); # proto'd to 1 hash ref
END { ... } # module clean-up code here (global destructor)
# YOUR CODE GOES HERE
1; # Don't forget to return a true value from the file!
Обекти
- Обект — променлива от определен тип, която си има
свойства (член-променливи) и върху която можем да
изпълняваме действия (методи)
- В Perl — обект — променлива, която знае от какъв
тип е, т.е. от кой пакет е. Дефинираните в него подпрограми
определят свойствата и методите на обекта.
- Винаги имаме reference към обекта, не работим със самата
променлива.
- В 99.9% от случаите променливата е хеш, макар че може да е
масив, скалар, парче код… всичко, към което можем да
насочим reference.
Обекти в Perl: реализация - bless (1/2)
- Обект — това е променлива, която знае към кой
пакет принадлежи.
- Класовете обикновено се пазят в модули, така че първо включваме
съответния модул:
- use Name::of::the::Module;
- bless $ref, name::of::package —
„кръщава“ обекта, сочен от $ref, и го асоциира с
пакета, подаден като втори параметър.
Обекти в Perl: реализация - bless (2/2)
- Методи се изпълняват с оператора „->“ (стрелка),
както в другите езици:
- При извикване на $obj->method(params)
интерпретаторът търси функция с име method
в пакета, към който принадлежи $obj, и
извиква тази функция с тези параметри, като за първи параметър
предава $obj.
- Можем (и често се случва) да „кръстим“ един обект
в пакет, различен от този, който го кръщава —
bless не ни ограничава с нищо.
Обекти: използване (1/2)
- Създаването на обект от даден клас, реално погледнато, се
свежда до bless-ването му, но често става с извикване на
специална подпрограма, наречена конструктор,
която извършва тази операция.
- Конструктор-подпрограмата по традиция (от C++) се кръщава new.
В Perl не е задължително конструкторът да се
казва new!
- Конструкторът връща reference, който ще използваме после.
Обекти: използване (2/2)
- На практика никога не се налага да дефинираме или извикваме
изрично деструктор — Perl ще се погрижи за освобождаване
на паметта в момента, в който не е останал нито един reference
към него.
- Можем да предаваме reference-а към обекта навсякъде, където
можем да предадем reference към нещо друго — това си е
обикновен reference.
Пример: използване на обекти
- use Lotto::Random;
$gen = new Lotto::Random;
$gen = Lotto::Random->new();
$gen = new Lotto::Random(Limit => 49);
$gen = Lotto::Random->new(Limit => 49);
$gen->set_limit(49);
print "The limit is ".$gen->get_limit()."\n";
$gen->limit(49);
print "The limit is ".$gen->limit()."\n";
print "The next number up is ".$gen->next()."\n";
@set = $gen->generate(6);
print "Six numbers: @set\n";
Пример: реализация на обекти
- package Lotto::Random;
sub new
{
# конструктор
# получава като първи параметър името на класа
my $proto = shift;
my $self = { 'limit' => 49 };
bless $self, $proto;
return $self;
}
sub next($)
{
my $self = shift;
return (int rand $self->{'limit'}) + 1;
}
Пример: реализация на обекти
- package Lotto::Random;
sub get_limit($)
{
return $_[0]->{'limit'};
# връща елемента 'limit' от хеша, сочен от първия
# параметър на get_limit(), който е самият обект
}
sub set_limit($ $)
{
my ($self, $value) = @_;
$self->{'limit'} = $value;
# достъп до обекта тук се осъществява през $self
}
Пример: реализация на обекти
- package Lotto::Random;
sub limit($ $)
{
my ($self, $value) = @_;
$self->{'limit'} = $value if defined $value;
return $self->{'limit'};
}
sub generate($ $)
{
my ($self, $count) = @_;
my @res;
@res = ();
push @res, $self->next() for (1..$count);
}
Пример: параметри на конструктора
- package Lotto::Random;
sub new
{
my ($proto, %params) = @_;
my $self = { };
$self->{'limit'} = $params{'limit'} || 49;
# $self->{'limit'} = defined($params{'limit'})?
# $params{'limit'} : 49;
# Perl 5.8.7 с defined-or:
# $self->{'limit'} = $params{'limit'} // 49;
bless $self, $proto;
return $self;
}
Пример: разширяем конструктор
- package Lotto::Random;
sub new
{
my ($proto, %params) = @_;
my $class = ref $proto || $proto;
my $self = { };
$self->{'limit'} = $params{'limit'} || 49;
bless $self, $class;
return $self;
}
$gen = new Lotto::Random(…);
$another = $gen->new(…);
# Actually useful:
$gen = new Lotto::Random::Or::Some::Derivative(…);
$another = $gen->new(…);
Използвани източници
- perltoot
- perlboot
- perlmod
- perlmodlib
- perlreftut