Первый сайт на Perl

4 months pregnant symptoms


Пакеты

В части 11 мы упомянули о том, что область действия переменных ограничена пакетом. Рассмотрим этот вопрос более подробно.

Итак, пакет — это способ создания собственного изолированного пространства имен для отдельного отрезка программы. Каждый фрагмент кода Peri-программы относится к некоторому пакету. Объявление

package NAMESPACE;

определяет пакет NAMESPACE. Ключевое слово package является именем встроенной функции, в результате обращения к которой компилятору предписывается использовать новое пространство имен. Область действия объявления пакета определяется аналогично области видимости локальных переменных, объявленных при помощи функций ту () или local (). Она распространяется либо до следующего объявления пакета, либо до конца одной из таких единиц программы:

  • Подпрограммы;
  • блока операторов, заключенного в фигурные скобки;
  • строки, переданной на выполнение функции eval ();
  • файла.

Область действия зависит от того, в каком месте вызвана для объявления пакета функция package о. Все идентификаторы, встретившиеся внутри этой области, принадлежат к пространству имен текущего пакета. Исключение составляют идентификаторы лексических переменных, созданных при помощи функции mу ().

Объявление пакета может быть сделано несколько раз в разных точках программы. Каждое объявление означает переключение на новое пространство имен. По умолчанию предполагается, что основная программа всегда начинается с объявления пакета

package main;

Таким образом, те переменные, которые мы называем в языке Perl глобальными, в действительности представляют собой переменные, чьи идентификаторы по умолчанию принадлежат пакету main.

К переменной из другого пакета можно обратиться, указав перед ее именем префикс, состоящий из имени этого пакета, за которым следуют два двоеточия: $PackageName: :name. Такие имена условимся называть квалифицированными именами. Если имя пакета отсутствует, предполагается имя main, т. е. записи $: :var и $main: :var обозначают одну и ту же переменную.

Специальная лексема _PACKAGE_ служит для обозначения имени текущего пакета.

#!/usr/bin/perl

$x=_PACKAGE_;

print "package $x:\n";

print "\$x= $x\n";

print "\$two::x= $two::x\n";

print "\$three::x= $three::x\n";

eval 'package two; $x=_PACKAGE_; print " package $x:\n"; print "\$x= $x\n"; print "\$main::x= $main::x\n"; print "\$three::x= $three::x\n";';

print "package $x:\n"; print "\$x= $x\n";

package three;

$x=_PACKAGE_;

print "package $x:\n";

print "\$x= $x\n";

print "\$main::x= $main::x\n";

print "\$two::x= $two::x\n";

package main;

print "package $x:\n";

print "\$x= $x\n";

print "\$two::x= $two::x\n";

print "\$three::x= $three::x\n";

В результате выполнения будут выведены следующие значения:

package main: $х= main

$two::x= $three::x=

package two: $x= two

$main::x= main $three::x=

package main: $x= main

package three: $x= three $main: :x= main $two::x= two

package main: $x= main $two::x= two $three::x= three

В данном примере используются три пакета, каждый со своим пространством имен: main, two, three. В каждом пакете определена переменная $х, значение которой совпадает с именем пакета. С пакетом main связаны следующие отрезки программы:

  • от начала программы до вызова функции evai ();
  • после вызова функции evai о до объявления пакета package three;
  • после явного объявления пакета package main до конца файла, содержащего данную программу.

Для выражения, выполняемого функцией evai (), определено собственное пространство имен two. Оно действует только в пределах этого выражения. Если бы внутри функции evai о не был определен собственный пакет two, все действия внутри нее были связаны с пакетом, в котором функция evai () была скомпилирована, т. е. с пакетом main.

С пакетом three связана часть программы от объявления package three до объявления package main.

В каждом пакете происходит обращение к переменным из двух других пакетов при помощи указания соответствующего префикса имени переменной.

Компилятор создает для каждого пакета отдельное пространство имен. Переменным $х из разных пакетов присваиваются их значения по мере выполнения соответствующего кода программы. Вот почему при первом обращении из пакета main к переменным two: :$x и $three: :x их значения еще не определены.  

Таблицы символов.

С каждым пакетом связана таблица символов. Она представляет собой хеш-массив, имя которого образовано из имени пакета, за которым следуют два двоеточия. Например, таблица символов пакета main хранится в хеш-массиве %main::. Ключами этого хеш-массива являются идентификаторы переменных, определенных в пакете, значениями — значения типа typeglob, указывающие на гнездо, состоящее из одноименных переменных разных типов: скаляр, массив, хеш-массив, функция, дескриптор файла или каталога.

Тип typeglob, с которым мы уже сталкивались в главе И — это внутренний тип данных языка Perl, который используется для того, чтобы при помощи одной переменной типа typeglob сослаться на все одноименные переменные разных типов. Признаком типа typeglob является символ "*". Если переменной типа typeglob присвоить значение другой переменной типа

typeglob:

*у = *х;

то для всех переменных с именем х: $х, @х, %х, &х, будут созданы псевдонимы $у, @у, %у, &у соответственно. Можно создать псевдоним только для переменной определенного типа, например, для скалярной:

*у = \$х;

Ключами в хеш-массиве таблицы символов являются все идентификаторы, определенные в пакете. Поэтому можно получить данные о переменных всех типов, определенных в пакете, проверяя значения элементов этого хеш-массива. Например, чтобы вывести имена всех переменных, определенных в пакете main, можно использовать следующий код.

* !/usr/bin/perl

my ($key, $item) ;

print "Таблица символов пакета main:\n";

for $key (sort keys %main::) {

local *myglob = $main::{$key};

print "определен скаляр \$$key = $myglob\n" if defined $myglob; i^'.Med @myglob) {

опт "определен массив \@$key :\n"; for $item (0..$#myglob) {

p> ••••••• \$$key [$item] = $myglob[$item] \n";

} }

if (defined %myglob) {

print "определен хеш-массив \%$key :\n";

for $item (sort keys %myglob) {

print "\$$key {$item} = $myglob{$item}\n";

} } print "определена функция $key()\n" if defined Smyglob;

}

При помощи типа typegiob можно создавать скалярные псевдоконстанты. Например, после присваивания

*Р1 = \3.14159265358979;

выражение $PI обозначает операцию разыменования ссылки на константу. Его значением является значение самой константы 3.14159265358979. Значение $PI нельзя изменить, так как это означало бы попытку изменить константу.

В таблицу символов пакета, отличного от main, входят только идентификаторы, начинающиеся с буквы или символа подчеркивания. Все остальные идентификаторы относятся к пакету main. Кроме того, к нему относятся следующие начинающиеся с буквы идентификаторы: STDIN, STDOUT, STDERR, ARGV, ARGVOUT, ENV, INC, sic. Например, при обращении внутри некоторого пакета pack к хеш-массиву %ENV подразумевается специальный хеш-массив %ENV основного пакета main, даже если имя main не используется в качестве префикса для обозначения принадлежности идентификатора ENV.

Конструктор и деструктор пакета ВEGIN и END

Конструктором в объектно-ориентированном программировании называется специальная подпрограмма, предназначенная для создания объекта. Деструктором называется подпрограмма, вызываемая для выполнения завершающих действий, связанных с ликвидацией объекта: закрытие файлов, вывод сообщений и т. д.

Для создания пакета, как мы знаем, требуется только его объявление (в том числе, предполагаемое по умолчанию объявление package main). Вместе с тем, существуют специальные подпрограммы, выполняющие функции инициализации и завершения пакета. По аналогии их можно назвать конструкторами и деструкторами пакета, хотя никаких пакетов они не создают и не удаляют. Это подпрограммы BEGIN и END. При описании этих подпрограмм ключевое слово sub, необходимое при объявлении обычной подпрограммы, можно опустить. Таким образом, /синтаксис, подпрограмм BEGIN, END имеет вид:

BEGIN (block) END {block}

Подпрограмма BEGIN выполняется сразу после своего определения до завершения компиляции оставшейся части программы. Попробуйте запустить интерпретатор peri в интерактивном режиме. Если ему передать строку

print "Привет!";

то он напечатает ее только после того, как обнаружит во входном потоке признак конца файла (например, комбинацию <Ctrl>+<D>). Если же в интерактивном режиме определить конструктор пакета

BEGIN {print "Привет!"};

то вывод строки "Привет!" будет осуществлен немедленно. Это свойство конструктора можно использовать, чтобы в начале пакета определять или импортировать имена из других пакетов. Затем эти имена будут влиять на процесс компиляции оставшейся части пакета.

Можно определить несколько блоков BEGIN внутри файла, они будут выполняться один за другим в порядке определения.

\ Подпрограмма END выполняется настолько поздно, насколько это возможно, т. е. при завершении работы интерпретатора. Можно указать несколько блоков END, при этом они будут выполняться в порядке, обратном определению.

END {

print "Завершаем работу, до свидания\п";

} . . BEGIN {

print "Привет, начинаем работу\n"; }

print "Это тело программы\n"; BEGIN {

print "Еще один блок BEGIN после блока END\n"; }

Здесь сознательно выбран не совсем естественный порядок следования конструкторов и деструкторов BEGIN и END в тексте программы, чтобы подчеркнуть, в каком порядке они будут вызываться. Вывод выглядит так:

Привет, начинаем работу

Еще один блок BEGIN после блока END

Это тело программы

Завершаем работу, до свидания  

Автозагрузка

При попытке обратиться к функции из некоторого пакета, которая в нем не определена, интерпретатор завершает работу с выдачей сообщения об ошибке. Если же в этом пакете определить функцию с именем AUTOLOAD, то при вызове из пакета несуществующей функции вместо нее будет вызвана функция AUTOLOAD с параметрами, переданными при вызове несуществующей функции. При этом интерпретатор peri продолжает выполнение программы. Полное имя несуществующей функции с указанием имени пакета сохраняется в переменной $AUTOLOAD из того же пакета, что и функция AUTOLOAD. Например, для основного пакета main можно определить функцию AUTOLOAD, как в следующем примере.

#!/usr/bin/perl sub AUTOLOAD {

print "Функция $AUTOLOAD не определена\п"; } print "Начало работы\п";

f();

print "Конец работы\n";

Функция f (), в отличие от функции AUTOLOAD, не определена в пакете main, поэтому в результате выполнения данной программы будут выведены сообщения:

Начало работы

Функция main::f не определена

Конец работы

Этот пример достаточно тривиальный, но он дает представление об использовании функции AUTOLOAD. В состав дистрибутива Perl входят стандартные модули, многие из которых содержат собственные, достаточно сложные определения функции AUTOLOAD, которые можно рассмотреть в качестве более сложного примера.

  Начало Вперед



Книжный магазин