Представьте, что у вас есть две программы, каждая из которых хорошо работает сама по себе. Возникает идея - создать третью программу, объединяющую лучшие свойства первых двух. Вы копируете обе программы в новый файл и начинаете перемещать фрагменты. Выясн
Проблема решается с помощью пакетов. Пакеты используются в Perl для разделения глобального пространства имен. Они образуют основу как для традиционных модулей, так и для объектно-ориентированных классов. Подобно тому, как каталог содержит файлы, пакет сод
Ключевое слово package является объявлением, обрабатываемым на стадии компиляции. Оно устанавливает префикс пакета по умолчанию для неполных глобальных идентификаторов, по аналогии с тем, как chdir устанавливает префикс каталога по умолчанию для относител
package Alpha;
$name = "first";
package Omega;
$name = "last";
package main;
print "Alpha is $Alpha::name, Omega is $0mega::name.\n";
Alpha is first, Omega is last. В отличие от пользовательских идентификаторов, встроенные переменные о специальными именами (например, $_ и $.) и идентификаторы STDIN, STDOLT. STDERR, ARGV, ARGVOUT, ENV, INC и SIG без указания имени пакета считаются принадлежащими к пакету main. Бла
Требуется определить внешний интерфейс модуля с помощью стандартного модуля Exporter.
Решение
Включите в файл модуля (например, YourModule.pm) приведенный ниже фрагмент. Многоточия заполняются в соответствии с инструкциями, приведенными в разделе "Комментарий".
package YourModule;
use strict;
use vars qw(@ISA OEXPORT @EXPORT_OK %EXPORT_TAGS $VERSION);
use Exporter;
$VERSION =1.00; # Или выше
@ISA = qw(Exporter);
@EXPORT = qw(...); # Автоматически экспортируемые имена
# (набор :DEFAULT)
@EXPORT_OK = qw(...); # Имена, экспортируемые по запросу
%EXPORT_TAGS = ( # Определение имен для наборов
TAG1 => [...],
TAG2 => [,..],
###################
# Ваш программный код
##################
1; # Так должна выглядеть последняя строка
Чтобы воспользоваться модулем YourModule в другом файле, выберите один из следующих вариантов:
use YourModule; # Импортировать в пакет имена по умолчанию
use YourModule qw(..,); # Импортировать в пакет перечисленные имена
use YourModule (); # Не импортировать никаких имен use YourModule
qw(:TAG1); # Импортировать набор имен
Комментарий
Внешний интерфейс модуля определяется с помощью стандартного модуля Exporter. Хотя в пакете можно определить собственный метод import, почти никто этого не делает.
Когда в программе встречается команда use YourModule, в действительности выполняется команда require "YourModule, pm", за которой вызывается метод ourModule->import(). Это происходит во время компиляции; Метод import, уна-юдованный из пакета Exporter, ище
$VERSION
При загрузке модуля можно указать минимальный допустимый номер версии.
ели версия окажется ниже, use инициирует исключение.
use YourModule 1.86 # Если $VERSION < 1.86, происходит исключение
Загружаемый модуль может отсутствовать в системе. Обычно это приводит к фатальной ошибке. Вы хотите обнаружить и перехватить эту ошибку.
Решение
Поместите require или use в eval, a eval - в блок BEGIN:
# Не импортировать
BEGIN {
unless (eval "require $mod") { warn "couldn't load $mod: $@";
}
}
# Импортировать в текущий пакет
BEGIN {
unless (eval "use $mod") { warn "couldn't load $mod: $@;
}
}
Комментарий
Попытка загрузки отсутствующего или неполного модуля обычно должна приводить к аварийному завершению программы. Однако в некоторых ситуациях программа должна продолжить работу - например, попытаться загрузить другой модуль. Как и при других исключениях, д
|рименяется конструкция eval.
Использовать eval { БЛОК } нежелательно, поскольку в этом случае будут перехватываться только исключения времени выполнения, a use относится к собы-шям времени компиляции. Вместо этого следует использовать конструкцию
val "СТРОКА", что позволит перехватывать и ошибки компиляции. Помните:
чызов require для простого слова' имеет несколько иной смысл, чем вызов require
"Простым словом" (barcword) называется слово, не имеющее специальной грамматической интерпретации и интерпретируемое как строка. - Примеч. перев.
для переменной. Команда добавляет расширение .рт и преобразует : : в раздел г тель каталогов вашей операционной системы - в каноническом варианте / (как в URL), но в некоторых системах используются \, : и даже . .
Если вы хотите последовательно попытаться загрузить несколько модулей и остановиться на первом работающем, поступите так:
BEGIN {
my($found, @DBs, $mod):
$found = 0;
ODBs = qw(Giant::Eenie Giant::Meanie Mouse::Mynie Мое);
for $mod (@DBs) {
if (eval "require $mod") {
$mod->import(); # При необходимости
$found = 1;
last-
}
}
die "None of @DBs loaded" unless $found:
} Мы включаем eval в блок BEGIN, чтобы гарантировать загрузку модуля во врс мя компиляции, а не во время выполнения.
> Смотри также
Рецепт 10.12; рецепт 12.3. Функции eval, die, use и require описаны в perlfunc(l).
Необходимо организовать загрузку модуля на определенной стадии p;i"n программы или вообще отказаться от его загрузки при некоторых обстоя! , ствах.
Решение
Разбейте use на отдельные компоненты require и import, либо воспольз\! . ь директивой use autouse.
Комментарий
Если программа проверяет свои аргументы и завершает работу с информационным сообщением или ошибкой, загружать неиспользуемые модули бессмысленно. Это лишь вызывает задержки и раздражает пользователей. Но как говори, юсь во введении, команды use обрабатыва
Наиболее эффективная стратегия состоит в проверке аргументов внутри блока BEGIN до загрузки модулей. Следующая программа перед загрузкой необходимых модулей проверяет, что она была вызвана ровно с двумя аргументами, каждый из которых является целым числом
BEGIN {
unless (OARGV == 2 && (2 == grep {/"\d+$/}
@ARGV)) { die "usage: $0 num1 num2\n";
}
}
use Some::Module;
use More::Modules;
Похожая ситуация возникает в программах, которые при разных запусках могут использовать разные наборы модулей. Например, программа factors из главы 2 "Числа" загружает библиотеку вычислений с повышенной точностью лишь при вызове с флагом -Ь. Команда u
if ($opt_b) {
require Math::BigInt;
} Math::BigInt является не традиционным, а объектно-ориентированным модулем, поэтому импортирование не требуется. Если у вас имеется список импортируемых объектов, укажите его в конструкции qw() так, как это было бы сделано для use. Например, вместо:
use Fcnti qw(0_EXCL 0_CREAT 0_RDWR);
можно использовать следующую запись:
require Fcnti;
Fcntl->import(qw(0_EXCL 0_CREAT 0_RDWR));
Откладывая импортирование до времени выполнения, мы сознательно идем на то, что оставшаяся часть программы не узнает об изменениях импортированной семантики, которые были бы видны компилятору при использовании use. В частности, не будут своевременно в
Возникает идея - инкапсулировать отложенную загрузку в подпрограмме. Следующее, простое на первый взгляд решение не работает:
sub load_module {
require $_[0]; # HEBEPHO
import $_[0]; # HEBEPHO
} Понять причину неудачи непросто. Представьте себе вызов require с аргументом "Math: : BigFloat". Если это простое слово, : : преобразуется в разделитель каталогов операционной системы, а в конец добавляется расширение .рт. Но простая переменная интерп
load_module('Fcntl', qw(0_EXCL 0_CREAT 0_RDVJR)),
sub load_module {
eval "require $_[0]":
die if $@;
$_[0]->import(@_[1 .. $"_]);
} Но и он в общем случае не идеален. Функция должна импортировать имена не в свой пакет, а в пакет вызвавшей стороны. В принципе эта проблема решается, но процедура становится все сложнее и сложнее.
Удобное альтернативное решение - применение директивы autouse. Он 1"1-явилась в Perl 5.004. Эта новая директива экономит время для редко загрузи мых функций, откладывая их загрузку до момента фактического использования:
use autouse Fcnti => qw( 0_EXCL() 0_CREAT() 0_RDWR() );
Круглые скобки после 0_EXCL, 0_CREAT и 0_RDWR нужны для autouse, но не для :se или import. Директива autouse принимает не только имена функций, но также позволяет передать прототип функции. В соответствии с прототипами константы Fcnti вызываются без аргум
Также помните, что проверка use strict осуществляется во время компи;!;: 1, :.i. Если модуль Fcnti подключается командой use, прототипы модуля Fcnti "' I,T откомпилированы и мы сможем использовать константы без круглых civ' '"к Если использована команда r
За сведениями об особенностях директивы autouse обращайтесь к электрсгшж документации.
> Смотри также -------------------------------
Рецепт 12.2; документация но стандартному модулю Exporter (описание метода import); документация по стандартной директиве use autouse.
Требуется сделать переменную или функцию закрытой (то есть разрепг, к' использование только в границах пакета).
Решение
Общего решения не существует. Однако можно ограничить доступ на уровне файла, в котором находится модуль, - обычно этого достаточно.
Комментарий
Помните, что пакет всего лишь определяет способ группировки переменных и функции и потому не поддерживает ограничения доступа. Все содержимое пакета по определению является глобальным и доступным отовсюду. Пакеты лишь группируют, ничего не скрывая.
Ограничение доступа возможно только с применением лексических переменных. Предположим, модуль реализован в виде файла Module.pm, а все его глобальные имена принадлежат пакету Module. Поскольку файл по определению образует самостоятельную область действия,
Однако переключение пакетов внутри области действия может привести к тому, что лексические переменные этой области остаются видны в любом месте области. Дело в том, что команда package всего лишь устанавливает новый префикс для глобальных идентификаторов.
package Alpha;
my $aa = 10;
$х = "azure";
package Beta;
my $bb = 20;
$x = "blue";
package main;
print "$aa, $bb, $x, $Alpha::x, $Beta::x\n";
10, 20, , azure, blue На это ли вы рассчитывали? Две лексические переменные, $аа и $bb, остаются в области действия, поскольку они не вышли за границы текущего блока, файла или eval. Считайте, что глобальные и лексические переменные существуют в разных изменениях, никак не
Итак, пакеты не позволяют ограничивать доступ - зато на это способны моду-.'щ, поскольку они находятся в файлах, а файл всегда обладает собственной областью действия. Приведенный ниже простой модуль находится в файле Flipper.pni ii экспортирует две функци
# Flipper.pm package Flipper;
use strict;
require Exporter;
use vars qw(@ISA OEXPORT $VERSION);
@ISA = qw(Exporter);
@EXPORT = qw(flip_words flip_boundary);
$VERSION =1.0;
my $Separatrix = ' '; # По умолчанию пробел; предшествует функциям
sub flip_boundary {
my $prev_sep = $Separatrix;
if (@_) { $Separatrix = $_[0] }
return $prev_sep;
} sub flip_words {
my $line = $_[0];
my @words = split($Separatrix, $line);
return join($Separatrix, reverse @words);
}
1; Модуль задает значения трех пакетных переменных, необходимых для работы Exporter, а также инициализирует лексическую переменную $Separatrix уровня файла. Как говорилось выше, эта переменная ограничивается границами файла, а не пакета. Весь код той же
Лексические переменные, существующие в некоторой области действия, нельзя прочитать или изменить вне этой области, которая в данном случае соответствует всему файлу после объявления переменной. На лексические переменные нельзя ссылаться по полному имени и
Работа приведенного выше модуля ничуть не изменилась бы, будь $Separa:.' ix пакетной глобальной переменной, а не файловой лексической. Теоретически к ней можно было бы обратиться снаружи так, что модулю об этом ничего не было известно. С другой стороны, н
Если уж речь зашла о стиле, регистр символов в идентификаторах модуля Flipper выбирался не случайно. В соответствии с руководством по стилю программирования на Perl. символами верхнего регистра записываются идентификаторы, имеющие специальное значение для
> Смотри также -------------------------------
perlstyle{1); рецепты 10.2-10.3. Лексические переменные с файловой областью действия рассматриваются в perlmod(1).
Текущий пакет определяется так:
$this_pack = __PACKAGE__; Пакет вызывающей стороны определяется так:
$that_pack = са11ег();
Комментарий
Метанеременная __PACKAGE__ возвращает пакет, в котором был откомпилирован :екущий код. Значение не интерполируется в строках, заключенных в кавычки:
print "I am in package __PACKAGE__\n"; # НЕВЕРНО! I am in package __PACKAGE__
Необходимость узнать пакет вызывающей стороны чаще возникает в старом годе, которому в качестве входных данных была передана строка для eval, файловый манипулятор, формат или имя манипулятора каталога. Рассмотрим гипотетическую функцию runit:
package Alpha;
runit('$line = ');
package Beta;
sub runit {
my $codestr = shift;
eval $codestr;
die if $@;
}
Такой подход работает лишь в том случае, если переменная $line является глобальной. Для лексических переменных он не годится. Обходное решение - сделать так, чтобы функция г u nit принимала ссылку на функцию:
package Beta;
sub runit {
my $codestr = shift;
my $hispack = caller;
eval "package $hispack; $codestr";
die if $@;
Новое решение не только работает с лексическими переменными, но и обладает дополнительным преимуществом - синтаксис кода проверяется во время компиляции, а это существенный плюс.
При передаче файлового манипулятора стоит воспользоваться более переносимым решением - функцией Symbol: : qualify. Она получает имя и пакет, для которого оно уточняется. Если имя нуждается в уточнении, оно исправляется, а в противном случае остается без и
Следующий пример читает и возвращает п строк из файлового манипулятора, Перед тем как работать с манипулятором, функция qualify уточняет его.
open (FH, "< /etc/termcap")
or die "can't open /etc/termcap: $!";
($a, $b, $c) = nreadline(3, 'FH');
use Symbol ();
use Carp;
sub nreadline {
my ($count, $handle) = @_;
my(@retlist,$line);
croak "count must be > 0" unless $count > 0;
$handle = Symbol::qualify($handle, (caller())[OJ);
croak "need open filehandle" unless defined fileno($handle);
push(@"retlist, $line) while defined($line = <$handle>) && $count--;
return Oretlist;
} Если при вызове функции nreadline файловый манипулятор всегда перелается в виде тип-глоба *FH, ссылки на глоб \*FH или с помощью объектов Filehr die или 10: : Handle, уточнение не потребуется. Оно необходимо лишь на случаи передачи минимального "FH".
> Смотри также -----------------------------
Документация по стандартному модулю Symbol; рецепт 12.12. Специальные метапеременные _FILE_, _LINE_ и _PACKAGE_ описаны в perldala(1),
Требуется создать для модуля начальный и завершающий код, вызываемый автоматически без вмешательства пользователя.
Решение
Начальный код реализуется просто - разместите нужные команды вне определений подпрограмм в файле модуля. Завершающий код помещается в блок END модуля.
Комментарий
В некоторых языках программист должен вызвать инициализирующий код модуля, прежде чем вызывать какие-либо его функции. Аналогично, при завершении программы от программиста может потребоваться вызов завершающего кода, выполняющего деинициализацию модуля.
В Perl дело обстоит иначе. Инициализирующий код модуля образуют команды, не входящие ни в одну подпрограмму модуля. Этот код выполняется непосредственно при загрузке модуля. Пользователю никогда не приходится следить за вызовом начального кода, поскольку
Для чего нужен автоматический вызов завершающего кода? Все зависит от модуля. Допустим, вам захотелось записать информацию о завершении в системный журнал, приказать серверу базы данных актуализировать все незаконченные операции, обновить состояние экрана
Предположим, модуль должен регистрировать начало и завершение своей работы в журнале. Вставьте следующий фрагмент в блок END, чтобы он выполнялся при завершении программы:
$Logfile = "/tmp/mylog" unless defined $Logfile;
open(LF, ""$Logfile")
or die "can't append to $Logfile: $!";
select(((select(LF), $|=1))[0]); # Отменить буферизацию LF
logmsg("startup");
sub logmsg {
my $now = scalar gmtime;
print LF "$0 $$ Snow: @_\n"
or die "write to $Logfile failed: $!";
END {
logmsg("shutdown");
close(LF)
or die "close $Logfile failed: $!
Первая часть кода, не входящая в объявления функций, выполняется во время загрузки модуля. Для этого от пользователя модуля не потребуется никаких специальных действий. Впрочем, для кого-нибудь это может оказаться неприятным сюрпризом, поскольку при недос
Блоки END не отличаются от других функций завершения - trap 0 в команд ном интерпретаторе, atexit в языке С или глобальные деструкторы в объектно-ориентированных языках. Порядок выполнения END противоположен порядку загрузки модулей; иначе говоря, первым
Однако с неперехваченными сигналами дело обстоит иначе. При завершении по сигналу блоки завершения не вызываются. Проблема решается следующей директивой:
use sigtrap qw(die normal-signals error-signals)
END также не вызывается в случае, если процесс вызывает функцию ехес, поскольку процесс остается тем же самым, изменяется лишь программа. Все стандартные атрибуты (идентификатор процесса и его родителя, идентификаторы пользователя и группы, маска доступа,
> Смотри также ------------------------------
Стандартная директива use sigtrap описана в perlmod(1), а переменная $"F в perldata(1). Функции fork и ехес рассматриваются в perlmod(1).