Некоторые сведения о Perl 5/Пакеты, библиотеки и модули
← Функции и процедуры | Глава | Объектно-ориентированное программирование в Perl → |
Пакеты, библиотеки и модули | ||
Пакеты
[править]Пакет — это пространство имен для некоторой части программы. Каждый фрагмент кода Perl относится к некоторому пакету.
Чтобы объявить пакет, используется конструкция
package <имя-пространства-имен>;
Встроенная функция package
предписывает компилятору использовать для оставшегося кода указанное пространство имен. Область действия этого пространства имен распространяется до следующего пакета, либо до конца:
- процедуры;
- блока;
- строки, переданной функции
eval()
; - исходного файла.
Все идентификаторы, встречающиеся после пакета, автоматически связываются с ним, кроме локальных переменных, созданных с помощью my()
.
Пространство имен можно многократно менять в разных точках программы. По умолчанию предполагается, что основная программа всегда начинается с объявления пакета package main;
. Таким образом, все глобальные переменные неявно находятся в пакете main
.
К переменным пакета main
мы можем обращаться без всяких дополнительных префиксов, но для обращения к объектам других пакетов нужно использовать следующий синтаксис
<идентификатор-типа><пространство-имен>::<имя-объекта>
# Например
$PackageName::var; # Обращение к скаляру в пространстве PackageName
# Для пространства main допустимо указывать пакет явно, но это
# в общем то бесполезно, если это пространство уже действует.
# Следующие три обращения направлены на одну и ту же переменную в пространстве
# main.
$var;
$main::var;
$::var;
С помощью специальной лексемы __PACKAGE__
можно узнать какое пространство имен применяется в некоторой точке программы.
Таблица символов пакета
[править]С каждым пакетом связана отдельная таблица символов. Она представляет собой хеш-массив, имя которого образовано из имени пакета, за которым следуют два двоеточия. Например, таблица символов для пакета main
хранится в хеш-массиве %main::
.
Ключами этого хеш-массива являются идентификаторы переменных, определенных в пакете, а значениями — typeglob
-ссылки, указывающие на гнездо, состоящее из одноимённых переменных разных типов.
Таблица символов может использоваться для создания псевдонимов, так как typeglob
-ссылки обладают следующими свойствами:
*alias = *object;
# alias становится псевдонимом для всех переменных одного гнезда
# таблицы: $object, @object, %object, &object.
# Можно создать псевдоним только для конкретного объекта
*alias = \$object; # Псевдоним для скаляра $object
Функция из следующего примера позволяет распечатать таблицу символов.
#
# Аргументы:
# $1 - имя пространства для печати
# \%2 - ссылка на хеш-массив пространства имен
#
# Пример:
# printNamespace ("main", %main::);
#
sub printNamespace ($\%) {
my ($spaceName, $space) = @_;
my ($key, $item);
print "Symbols in the '$spaceName' namespace:\n";
for $key (sort keys %$space) {
local *glob = $$space{$key};
print " scalar: \$$key = $glob\n" if defined $glob;
if (@glob) {
print " array '\@$key':\n";
for $item (0..$#glob) {
print " \$$key [$item] = $glob[$item]\n";
}
}
if (%glob) {
print " hash '\@$key':\n";
for $item (sort keys %glob) {
print " \$$key {$item} = $glob{$item}\n";
}
}
print " routine '$key()'\n" if defined &glob;
}
}
package test1;
$scalar = 4;
@array = (1, 2, 3);
%hash = (name => "Larry", surname => "Wall");
sub func { return 0 }
package test2;
$scalar = 4;
@array = (1, 2, 3);
%hash = (name => "Larry", surname => "Wall");
sub func { return 0 }
package main;
printNamespace ("test1", %test1::);
printNamespace ("test2", %test2::);
Результат вывода
Symbols in the 'test1' namespace:
array '@array':
$array [0] = 1
$array [1] = 2
$array [2] = 3
routine 'func()'
hash '@hash':
$hash {name} = Larry
$hash {surname} = Wall
scalar: $scalar = 4
Symbols in the 'test2' namespace:
array '@array':
$array [0] = 1
$array [1] = 2
$array [2] = 3
routine 'func()'
hash '@hash':
$hash {name} = Larry
$hash {surname} = Wall
scalar: $scalar = 4
В этом примере мы создали два пакета test1
и test2
, в каждом из которых мы объявили скаляр, массив, хеш и функцию.
В таблицу символов пакета, отличного от main
, входят только идентификаторы, начинающиеся с буквы или символа нижнего подчеркивания. Все остальные идентификаторы относятся к пакету main
. Кроме них к main
также относятся: STDIN
, STDOUT
, STDERR
, ARGV
, ARGVOUT
, ENV
, INC
, SIG
. В этих случаях не обязательно указывать пространство имен из другого пространства.
Конструктор и деструктор пакета
[править]В некоторых сложных пакетах может потребоваться внутренняя инициализация, например установка констант, некоторые переключения, зависящие от операционной системы, управление порядком компиляции и прочее. Для таких случаев в пакет может быть внедрен конструктор и деструктор пакета.
Конструктор пакета — это процедура, оформленная в специальном блоке BEGIN
, которая выполняется сразу после своего определения до завершения компиляции оставшейся части программы.
Деструктор пакета — это процедура, оформленная в специальном блоке END
, которая выполняется перед выгрузкой пакета.
BEGIN { <операторы> }
END { <операторы> }
Для одного пакета может быть определено несколько блоков BEGIN
и END
, причем блоки END
могут быть определены раньше BEGIN
: это не повлияет на порядок их вызова. Однако, порядок определения блоков-конструкторов и порядок определения блоков-деструкторов будет определять порядок их вызова.
Следующий пример демонстрирует, как вызываются конструкторы и деструкторы пакетов.
BEGIN {
print "Package " . __PACKAGE__ . " are loading ...", "\n";
}
END {
print "Package " . __PACKAGE__ . " are unloading ...", "\n";
}
END {
print __PACKAGE__ . ": additional destructor.", "\n";
}
package test1;
BEGIN {
print "Package " . __PACKAGE__ . " are loading ...", "\n";
}
BEGIN {
print __PACKAGE__ . ": additional consturctor.", "\n";
}
END {
print "Package " . __PACKAGE__ . " are unloading ...", "\n";
}
Результат вывода
Package main are loading ...
Package test1 are loading ...
test1: additional consturctor.
Package test1 are unloading ...
main: additional destructor.
Package main are unloading ...
Автозагрузка
[править]При попытке обратиться к функции, которая еще не определена в пакете, интерпретатор завершает работу с выдачей сообщения об ошибке. Если в пакете определить функцию со специальным именем AUTOLOAD
, то интерпретатор будет перенаправлять вызов на нее с сохранением передаваемых параметров. Кроме того, внутри этой функции будет определен скаляр $AUTOLOAD
, в котором будет хранится имя вызываемой функции.
Способы применения этой возможности самые различные. Самое простое, так можно отслеживать вызов функции при написании прототипа новой программы или определить, что пакеты подгружаются в программу неправильно.
sub AUTOLOAD {
print "Called '$AUTOLOAD': @_\n";
}
testCall(1, 2 ,4);
undefinedFunc(1, "a", 3.1415, "b");
Результат
Called 'main::testCall': 1 2 4
Called 'main::undefinedFunc': 1 a 3.1415 b
Библиотека
[править]Пакеты, как мы выяснили это выше, служат для повторного использования имен, а также для логического объединения операторов. Второй шаг — это группировка процедур и сохранение их в отдельных исходных файлах.
Для реализации этого, в Perl существует два механизма: библиотеки и модули. В этом разделе мы рассмотрим библиотечный подход.
Библиотеки появились в Perl с 4-й версии языка и являются классическим методом. Библиотека — это пакет, областью действия которого является отдельный файл. Другими словами, библиотека Perl — это исходный файл, составленный на языке Perl, первая строка которого вводит пространство имен:
package package_name;
Имя библиотечного файла обычно имеет расширение .pl
.
Основная программа загружает библиотеки при помощи директивы компилятора require
.
Загрузка библиотеки
[править]Директива
require [<выражение>]
служит для загрузки функций из внешних библиотек во время выполнения. Если <выражение>
отсутствует, то вместо него будет использоваться переменная $_
.
Если <выражение>
является числом, то вызов соответствует требованию, что для выполнения данного сценария необходим интерпретатор perl с номером версии, не меньшим, чем указано. Самую меньшую версию, которую можно указать, это 5.005. Более ранние интерпретаторы будут прерывать исполнение с выдачей сообщения об ошибке.
Чтобы загрузить библиотеку, нужно указать имя исходного файла в выражении. Логику работы этой директивы можно выразить следующей псевдофункцией:
sub require {
my ($filename) = @_;
return 1 if $INC{$filename};
my ($realfilename, $result);
ITER: {
foreach $prefix (@INC) {
$realfilename = "$prefix/$filename";
if (-f $realfilename) {
$result = do $realfilename;
last ITER;
}
}
die "Can't find $filename in \@INC";
}
die $@ if $@;
die "$filename did not return true value" unless $result;
$INC{$filename} = $realfilename;
return $result;
}
Специальный встроенный массив @INC
содержит имена каталогов, в которых следует искать сценарии Perl, подлежащие выполнению в конструкциях:
do <файл>
;require
;use
.
В этот массив помещаются каталоги, переданные интерпретатору опцией -I
, стандартные пути к библиотечным каталогам (которые могут зависеть от операционной системы) и символическое обозначение текущего каталога .
.
Специальный встроенный хеш-массив %INC
содержит по одному элементу для каждой загруженной библиотеки с помощью do
и require
. Ключом является имя файла, в том виде, в котором оно было указано, а значением — полный путь к нему. Хеш-массив %INC
защищает программу от повторной загрузки библиотеки, так как он проверяется перед включением.
Обычно библиотечные файлы имеют расширение .pl
в своем имени. Так как в Perl есть операция конкатенации .
, чтобы не путать интерпретатор, следует всегда передавать имена файлов в кавычках:
require "mylib.pl";
Если файл не имеет расширения, то по умолчанию предполагается, что это модуль, которые имеют расширение .pm
.
Рекомендации по созданию библиотечных файлов
[править]Для создания библиотеки нужно придерживаться следующих правил:
- Создайте каталог для хранения библиотечных файлов и помещайте файлы в него по мере роста библиотеки.
- Типовая струтура библиотечного файла имеет вид:В конце файла следует поместить строку
package myspace; ... # Процедуры, функции и другое ... 1;
1;
, которая нужна для реализации вызоваdo <файл>
: несложно догадаться, что еслиdo
удается распарсить файл целиком, то наружу нужно вернуть ИСТИНУ, которая как раз возвращается единицей. - Библиотеки следует включать в основном директивой
require
. - Пути поиска пользовательских библиотек можно внедрять в программу по-разному:
- Раньше
@INC
обычно редактировался в конструкторе пакета как то так:Сейчас рекомендуется использовать директивуBEGIN { unshift(@INC, <список>) }
lib
(см. ниже). - Можно передавать пути к каталогам через опцию
-I
.
- Раньше
Модуль
[править]Модули являются дальнейшим развитием библиотек и появились начиная с Perl 5. Модуль — это библиотека, оформленная особым образом, которая определяет свои правила включения.
Модуль определяет правила экспорта и импорта. Правила экспорта предоставляют другим модулям права для импорта в них символов текущего модуля. В свою очередь, правила импорта определяют правила включения в текущий модуль символы из других модулей. Таким образом, в такой системе ожидается, что модули могут включать друг друга единственно правильным образом, а все ошибочные действия будут перехватываться как можно раньше.
Для целей управления экспортом каждый модуль должен располагать методом import()
и определить специальные массивы @EXPORT
и @EXPORT_OK
. Вызываюшая программа обращается к методу import()
, чтобы включить в себя модуль. Массив @EXPORT
содержит идентификаторы, экспортируемые по умолчанию. Специальный массив @EXPORT_OK
содержит идентификаторы, которые будут экспортироваться только в том случае, если они указаны явно в списке импорта вызывающей программы.
С появлением модулей появилась и новая директива для их подключения к основной программе — use()
.
Директива use()
[править]Директива
use <модуль> [<список>]
use <версия>
служит для того же самого, что и require()
, но ориентирована на загрузку модулей. Она автоматически импортирует имена функций и переменных в основное пространство имен текущего пакета. Для этого в импортируемом модуле неявно вызывается метод import()
. Этот метод должен быть определен в экспортирующем модуле, либо он должен его унаследовать у модуля Exporter
. Большинство модулей не имеют своего метода import()
, поэтому они наследуют его из модуля Exporter
.
Логику работы директивы use
можно описать строкой:
BEGIN { require <модуль>; import <модуль> <список>; }
Здесь <модуль>
это слово без суффиксов, не заключенное в кавычки. Если аргументом является число <версия>
, то оно будет обозначать номер версии интерпретатора perl. Если номер версии текущего интерпретатора меньше указанного, то интерпретатор завершит работу с сообщением об ошибке.
Если при импорте модуля указан <список>
, то будут импортироваться символы, указанные в этом списке, иначе будут импортироваться символы из массива @EXPORT
, определенном в самом модуле.
Создание и подключение модуля
[править]Для создания модулей требуется немного больше кода, чем для простых библиотек. Ниже показан пример модуля, который можно использовать как шаблон.
# Файл: TestModule.pm
package TestModule; # Пространство модуля
require Exporter; # Импортируем метод import()
@ISA = qw{ Exporter }; # Для наследования из @ISA
@EXPORT = qw{ test printContent }; # Символы, экспортируемые по умолчанию
@EXPORT_OK = qw{ $scalar @array %hash }; # Символы, экспортируемые по требованию
# Функции модуля
sub test {
my ($element, $counter);
@array = @_;
$scalar = $#array + 1;
foreach $element (@array) {
$hash{"$element"} = ++$counter;
}
}
sub printContent {
foreach $key (sort keys %hash) {
print __PACKAGE__ . "::\$hash {$key} = $hash{$key}\n";
}
print __PACKAGE__ . "::Scalar = $scalar\n";
}
1;
Первые пять строк (начиная со второй) являются стандартными для любого модуля:
package TestModule;
Вводит пространство имен модуляTestModule
, исходный файл которого имеет имяTestModule.pm
.require Exporter;
Мы подключаем библиотекуExporter
, чтобы не писать методimport()
.@ISA = qw{ Exporter };
С каждым пакетом ассоциируется свой массив@ISA
, включающий имена других пакетов, представляющих классы. Если интерпретатор наталкивается на метод, не определенный в текущем пакете, он просматривает массив@ISA
, чтобы найти его среди перечисленных пакетов. В нашем примере достаточно указать только модульExporter
.@EXPORT = qw{ test printContent };
Массив символов, которые импортируются из модуляTestModule
неявно. В данном случае мы всегда неявно импортируем обе функции, которые определены в модуле.@EXPORT_OK = qw{ $scalar @array %hash };
Массив символов, которые мы разрешаем импортировать из модуляTestModule
по требованию списком. В данном примере мы разрешаем импортировать все глобальные переменные модуля. На практике, обычно, модуль имеет свои внутренние переменные, которые он никому не раскрывает.
Представим, что наш тестовый модуль лежит в некотором каталоге. Теперь напишем программу, которая его подключит и вызовет его функции.
# Файл: main.pl
use TestModule qw{ :DEFAULT $scalar };
test one, two, three, four;
printContent;
print __PACKAGE__ . ": TestModule::Scalar = $scalar\n";
Первой строкой use TestModule qw{ :DEFAULT $scalar };
мы подключаем модуль TestModule
. Кроме имени модуля, мы передаем список того, что хотим импортировать в наше приложение. В нашем примере мы передаем спецификацию :DEFAULT
, которая требует импортировать все, что перечислено в массиве @EXPORT
, а также дополнительно мы просим импортировать символ $TestModule::scalar
.
Далее мы вызываем функцию модуля test()
, которая инициализирует внутренние массивы модуля переданными значениями, а затем мы вызываем функцию печати printContent()
, которая печатает содержимое хеш-массива модуля. Благодаря тому что мы импортировали символ $TestModule::scalar
, мы можем им пользоваться в нашем приложении, как будто он существует в пространстве main
.
Вызывать нашу программу мы должны с опцией -I
, так как наш модуль не лежит в стандартном для модулей месте. Таким образом, вызов должен быть таким:
$ perl -I$(pwd) ./main.pl
Результат работы программы представлен ниже
TestModule::$hash {four} = 4
TestModule::$hash {one} = 1
TestModule::$hash {three} = 3
TestModule::$hash {two} = 2
TestModule::Scalar = 4
main: TestModule::Scalar = 4
Директива no()
[править]Директива
no <имя-модуля> <список>
является противоположной по смыслу директиве use()
. Она отменяет действия, связанные с импортом, вызывая метод unimport <имя-модуля> <список>
.
Тонкости экспорта модулей
[править]Чтобы директива use
отрабатывала правильно, первой строкой модуль должен вводить пространство имен директивой package
, причем имя пространства всегда должно совпадать с именем файла (без учёта расширения). Похожий подход используется в языке Java, где имя файла совпадает с именем первого публичного класса внутри файла исходного кода. В таком случае директива всегда неявно пытается найти и вызвать функцию import()
внутри модуля.
Например, если бы у нас существовал собственный не вложенный модуль с именем MyModule
, то программа, вызывающая его, должна в служебном массиве @INC
хранить директорию, в которой находится файл MyModule.pm
, а директива use
внутри вызывающего кода могла бы выглядеть так:
# Код вызывающей программы
# ...
# Мы передаем модулю список аргументов.
use MyModule qw{ arg1 arg2 arg3 };
# ...
no MyModule qw{ arg1 arg2 arg3 };
Код самого модуля должен быть примерно таким:
# Код внутри файла MyModule.pm
package MyModule;
# ...
sub import {
# $self хранит имя модуля, а массив @args - переданные аргументы.
my ( $self, @args ) = @_;
print "import '$self'\n";
for (@args) {
print " " . $_ . "\n";
}
# ...
}
sub unimport {
my ( $self, @args ) = @_;
print "unimport '$self'\n";
for (@args) {
print " " . $_ . "\n";
}
}
# ...
Во время экспорта модуля директивой use
, она неявно найдет функцию import()
и передаст ей аргументы
import 'MyModule' arg1 arg2 arg3
Аналогично с директивой no
— она будет неявно вызывать функцию unimport()
:
unimport 'MyModule' arg1 arg2 arg3
Обычно в качестве аргументов экспорта модуля, программист передает имена символов, которые должны быть загружены в таблицу символов вызывающего кода, но это не обязательно. В некоторых случаях вы можете передавать флаги или инструкции по режиму загрузки/выгрузки модуля.
До появления модуля Exporter
разработчики часто писали свою реализацию метода import()
. В большинстве случаев она будет похожа на что-то типа такого:
package MyModule;
use strict;
sub import {
my ( $self, @args ) = @_;
my $caller = caller();
# Отключаем ограничение, чтобы иметь возможность разрешать символические ссылки на функции.
no strict "refs";
for my $arg (@args) {
# Экспортируем функции текущего модуля в вызывающий его код путем
# определения в таблице символов вызывающего кода ссылок на функции текущего модуля.
# Мы это реализуем через символические ссылки, которые получаются конкатенацией
# имени пакета модуля с именем символа, переданного модулю. Ожидается, что
# клиентский код правильно передает имена, иначе такой трюк не пройдет.
*{"${caller}::${arg}"} = sub {
return &{__PACKAGE__ ."::$arg"};
};
}
}
#...
sub function1 {
print "Function 1\n";
}
sub function2 {
print "Function 2\n";
}
1;
Тогда вызывающий код может импортировать функции модуля в своё пространство так:
#!/usr/bin/perl
use strict;
# Обратите внимание, что мы импортировали только одну функцию модуля.
use MyModule qw { function1 };
eval {
&function1;
&function2;
};
print $@ if $@;
Вывод будет таким
Function 1 Undefined subroutine &main::function2 called at run.pl line 9.
Обратите внимание, что вызов первой функции модуля отрабатывает нормально, а второй вызов завершился аварийно, потому что function2
мы не импортировали.
В реальных программах следует отдавать предпочтение использованию модуля Exporter
(см. документацию к модулю), так как он реализует множество тривиальных проверок. Свою реализацию нужно делать, только если этого модуля в дистрибутиве Perl нет, либо экспорт символов очень особенный.
Сложный модуль с вложенностями
[править]Модули могут состоять из множества файлов, т.е. модуль может быть совокупностью меньших модулей. Разработчик обычно сохраняет Perl-код сложного модуля в отдельный каталог, в котором меньшие модули также могут быть разложены по каталогам для создания вложенности и структуры. Нет каких-то строгих правил, как разработчик это будет делать, но обычно с модулем поставляется так называемый загружающий его файл, который внутри себя организует инициализацию всего модуля, а прочие файлы модуля могут быть включены в клиентский код только через него. Другой ситуацией может быть, когда модуль состоит из нескольких обособленных подмодулей, связанные одной темой, но подключаемые через свои загружающие файлы.
Для создания сложной иерархии, Perl заимствует идеи из языков программирования C++ и Java. Из Java берется идея использования структуры файловой системы для создания подобия Java-пакетов для исходных файлов, а у C++ способ создания вложенности у пространств имен. Для создания вложенной структуры у модуля, необходимо следовать следующим правилам:
- Размещайте код модуля в каталоге, имя которого должно состоять из букв и цифр, и не должно начинаться на цифру. Имя каталога будет верхним уровнем пространства имен модуля. Это аналогично тому, как классы размещаются в пакетах на языке Java. Соответственно имена модулей лучше начинать с большой буквы.
- Если подмодуль размещается в некотором каталоге (вложенном пакете), то в имени его пространства имен необходимо использовать два символа двоеточия
::
чтобы выразить вложенность, аналогично тому, как это делается в C++. Например, записьpackage MyModule::SubModule::Util;
означает, что файлUtil.pm
должен быть расположен по путиMyModule/SubModule/Util.pm
. - Чтобы сложный модуль можно было подключить, необходимо, чтобы в служебном массиве
@INC
был путь к директории, являющейся родительской по отношению к каталогу модуля.
Базовые модули Perl
[править]В стандартной поставке Perl включено множество стандартных модулей, некоторые из которых мы перечислим в приложении (см. Некоторые сведения о Perl 5/Приложения#Библиотечные модули Perl). Помимо них, за много лет существования Perl, была создана огромная коллекция модулей CPAN (Comprehensive Perl Archive Network), доступ к которой можно получить через Интернет. Обычно вы должны сначала посмотреть не была ли решена ваша задача в виде некоторого модуля CPAN. Если такой модуль существует, вам следует загрузить его и установить в своей системе. Исходные тексты всех модулей Perl распространяются открыто, поэтому вы всегда сможете познакомиться с тем, как они работают.
Прагма-библиотеки
[править]Компилятором Perl можно управлять с помощью специальных модулей, объявляющих особые директивы. Данные директивы обычно используются чтобы запретить некоторые конструкции языка Perl (ужесточить проверки, чтобы сделать код более предсказуемым), выводить предупреждения или отладочные сообщения и многое другое. Такие библиотеки мы называем библиотеками директив компилятора или просто прагма-библиотеками (pragmas).
Как и любые другие модули, директивы подключаются с помощью use()
и отключаются с помощью no()
.
Обычно директива применяется ко всему пакету/библиотеке/модулю, либо директивы применяют к отрезку программы. Например, чтобы ускорить вычисления, директивой integer
можно временно запретить вычисления с плавающей точкой, оставив только целочисленные:
print 2 / 3, "\n"; # 0.666666666666667
use integer;
print 2 / 3, "\n"; # 0
no integer;
print 2 / 3, "\n"; # 0.666666666666667
В дистрибутивный комплект Perl входит стандартный набор прагма-библиотек. Некоторые из них мы рассмотрим ниже.
С прагма-библиотеками следует быть осторожными, так как от релиза к релизу они могут менять поведение. Большинство учебников по Perl не успевают за этими изменениями, поэтому сведения из очень старых уже не актуальны для последних релизов языка. Этим мы хотим сказать, что не ленитесь сверяться с документацией к конкретному релизу на perldoc.perl.org.
Директива strict
[править]Очень полезная директива, которая запрещает использование небезопасных конструкций языка. Многие подходы в программировании на Perl, которые в старых релизах интерпретатора считались допустимыми, со временем стали небезопасными из-за возможности возникновения скрытых ошибок. В новых релизах документация к Perl обычно рекомендует новые подходы, не запрещая старые для сохранения обратной совместимости.
Чтобы запретить использование старых методов и не давать искушения программисту их где-либо использовать в новом коде, следует в начале любого исходного файла вводить эту директиву. Директива обычно применяется к файлу или отдельному блоку.
use strict;
# Аналогично следующим трем вызовам:
# Запрещает объявление переменных без явного указания их области видимости
use strict "vars";
# Запрещает символические ссылки
use strict "refs";
# Ужесточает правила для идентификаторов:
# если идентификатор не заключен в фигурные скобки и не стоит
# слева от =>, то нельзя его записывать без кавычек и без идентификатора типа.
# При этом, если он является именем процедуры, то это ошибкой не считается.
use strict "subs";
Ниже приведены антипримеры того, что запрещает директива.
# Примечание: везде, где написано НЕЛЬЗЯ, оператор запретит директива.
use strict 'refs';
$ref = \3.1415;
print $$ref, "\n"; # МОЖНО: разыменовывается жесткая ссылка.
$ref = "foo";
print $$ref, "\n"; # НЕЛЬЗЯ: попытка разыменовать символическую ссылку.
$file = "STDOUT";
print $file "Hi!"; # НЕЛЬЗЯ: без запятой после $file и без директивы, интерпретатор
# попытался бы разрешить символическую ссылку, однако сейчас они
# запрещены директивой, поэтому эта строка не скомпилируется.
# ПРАВИЛЬНО
$file = *STDOUT;
print $file "Hi!";
# Есть одно ИСКЛЮЧЕНИЕ из правил.
$bar = \&{'foo'};
&$bar; # МОЖНО: ссылка на функцию разрешится как символическая.
# Примечание: везде, где написано НЕЛЬЗЯ, оператор запретит директива.
use strict 'vars';
$main::var = 1; # МОЖНО: есть квалификатор пакета
$var1 = 2; # НЕЛЬЗЯ: область видимости не указана явно.
our $var1 = 2; # МОЖНО: our определяет область видимости пакета.
my $lexical = 3; # МОЖНО: my определяет лексическую область видимости.
local $var1 = 4; # МОЖНО: потому что переменная $var1 была к этому моменту определена.
local $var2 = 5; # НЕЛЬЗЯ: переменная $var2 должна быть определена к этому моменту,
# даже несмотря на то, что local определяет видимость.
package MyPackage;
our $var3 = 6; # МОЖНО: our определяет область видимости пакета.
# Примечание: везде, где написано НЕЛЬЗЯ, оператор запретит директива.
use strict 'subs';
sub func { return 0 }
$scalar = string; # НЕЛЬЗЯ: нужны кавычки для литерала.
$scalar = "string"; # ПРАВИЛЬНО
func 3, arg2; # НЕЛЬЗЯ: второй аргумент должен быть закавычен.
func 3, "arg2"; # ПРАВИЛЬНО
$ref = \&func; # МОЖНО: потому что func это имя функции.
До версии интерпретатора 5.36 эту директиву нужно было прописывать всегда явно. Начиная с версии 5.36 достаточно писать следующим образом:
use v5.36; # Директива 'strict' включается неявно.
...
Здесь же отметим, что до недавнего времени (а конкретнее до Perl 5.6.0) при включенной use strict 'vars';
, глобальные переменные нужно было объявлять через директиву vars
, например
use vars qw($frob @mung %seen);
С версии Perl 5.6.0 глобальные переменные пакета следует объявлять исключительно через функцию our
.
Директива lib
[править]Директива lib
позволяет редактировать массив @INC
из программы. Напомним, что этот массив используется для определения путей, по которым нужно искать модули и библиотеки.
# Добавит список к тому, что уже есть в массиве.
use lib <список-директорий>;
# Очистит @INC целиком
no lib;
Вы можете только добавлять конкретные пути в массив. С помощью директивы удалить можно только сразу все пути. Следует аккуратно удалять пути, так как некоторыми из них могут пользоваться уже загруженные модули.
Вернемся к примеру, где мы писали свой модуль. В основной программе мы могли бы добавить нужные директивы для поиска модуля.
use strict; # Добавим директиву, чтобы ужесточить проверки.
use lib qw{ . }; # Добавим текущую рабочую директорию в @INC.
use TestModule qw{ :DEFAULT $scalar };
test "one", "two", "three", "four";
printContent;
print __PACKAGE__ . ": TestModule::Scalar = $scalar\n";
Директива subs
[править]Данная директива позволяет определить процедуры до их фактического определения. В основном это используется для того, чтобы вызывать их без скобок вокруг аргументов и без идентификатора &
.
use subs <список-имен-процедур>;
Пример
example1 1, 2, 3; # НЕЛЬЗЯ: функция еще к этому моменту не определена.
example1 (1, 2, 3); # ПРАВИЛЬНО: в этой точке скобки обязательны.
use subs qw{ example1 example2 example3 };
example1 1, 2, 3; # МОЖНО: функция как бы определена к этому моменту через директиву.
sub example1 { return 0 }
sub example2 { return 0 }
sub example3 { return 0 }
Директива parent
[править]Используется, чтобы работать с массивом @ISA
не напрямую, а через директиву. Позволяет добавлять модули в массив @ISA
, чтобы, как это было описано выше, искать функции в родительских модулях, если их нет в текущем.
Например, запись
package CurrentModule;
use parent qw { ParentModule_1 ParentModule_2 };
аналогична
package CurrentModule;
BEGIN {
require ParentModule_1;
require ParentModule_2;
push @ISA, qw(ParentModule_1 ParentModule_2);
}
По умолчанию ожидается, что родительские модули лежат в разных файлах, но пространства родителей могут лежать в том же файле что и модуль (обычно в отладочных целях). Чтобы иметь возможность подключить их код директивой, нужно передать ей параметр -norequire
, чтобы не использовать механизм require
для поиска кода:
package ParentModule_1;
# ...
package ParentModule_2;
# ...
package CurrentModule;
use parent -norequire, 'ParentModule_1', 'ParentModule_2';
что аналогично записи
# ...
push @CurrentModule::ISA, 'ParentModule_1', 'ParentModule_2';
Флаг также может использоваться, когда файл библиотеки подключается явным require
, но пространство имен внутри родителей имеет имя отличное от имени их файла. Обычно так стараются не делать, но такие ситуации тоже можно обходить.
Данная директива имеет более старую версию base
, которая работает похожим образом, но ей не рекомендуется пользоваться, так как она ориентирована на работу совместно с директивой fields
, что ухудшает её производительность.
← Функции и процедуры | Объектно-ориентированное программирование в Perl → |