ЭЛЕКТРОННАЯ БИБЛИОТЕКА КОАПП |
Сборники Художественной, Технической, Справочной, Английской, Нормативной, Исторической, и др. литературы. |
Пакеты, библиотеки и модулиВведениеПредставьте, что у вас есть две программы, каждая из которых хорошо работает сама по себе. Возникает идея - создать третью программу, объединяющую лучшие свойства первых двух. Вы копируете обе программы в новый файл и начинаете перемещать фрагменты. Выясняется, что в программах встречаются переменные и функции с одинаковыми именами, которые невозможно объединить. Например, каждая программа может содержать функцию in it или глобальную переменную $count. При объединении эти компоненты вступают в конфликт. Проблема решается с помощью пакетов. Пакеты используются в Perl для разделения глобального пространства имен. Они образуют основу как для традиционных модулей, так и для объектно-ориентированных классов. Подобно тому, как каталог содержит файлы, пакет содержит идентификаторы. Каждый глобальный идентификатор (переменная, функция, манипулятор файла или каталога, формат) состоит из двух частей: имени пакета и собственно идентификатора. Эти две части разделяются символами : :. Например, переменная $CGI: :needs_binmode представляет собой глобальную переменную с именем $needs_binmode, принадлежащую пакету CGI (до выхода версии 5.000 для этой цели использовался апостроф - например, $CGI' needs_bin_mode). Переменная $Names: : startup - это переменная $startup пакета Names, a $Dates:: startup - переменная $startup пакета Dates. Идентификатор $startup без имени пакета означает глобальную переменную $startup текущего пакета (при условии, что в данный момент не видна лексическая переменная $startup; о лексических переменных рассказано в главе 10 "Подпрограммы"). При указании неполного имени (то есть имени переменной без пакета) лексические переменные переопределяют глобальные. Лексическая переменная существует в области действия; глобальная - на уровне пакета. Если вам нужна глобальная переменная, укажите ее полное имя. Ключевое слово package является объявлением, обрабатываемым на стадии компиляции. Оно устанавливает префикс пакета по умолчанию для неполных глобальных идентификаторов, по аналогии с тем, как chdir устанавливает префикс каталога по умолчанию для относительных путей. Влияние package распространяется до конца текущей области действия (блока в фигурных скобках, файла или eval) или до ближайшей команды package в той же области действия (см. следующий фрагмент). Все программы выполняются в пакете main, пока командой package в них не будет выбран другой пакет.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. Благодаря этому STDIN, @ARGV, %ENV и $_ всегда означают одно и то же независимо от текущего пакета; например, @ARGV всегда относится I; @main: :ARGV, даже если вы измените пакет по умолчанию командой package. Уточненное имя @ElseWhere: :ARGV относится к нестандартному массиву @ARGV и не обладает специальным значением. Не забудьте локализовать переменную $_. если вы используете ее в своем модуле. МодулиМногократное использование кода в Perl осуществляется с помощью модулей Модуль представляет собой файл, содержащий набор взаимосвязанных функций, которые используются другими программами и библиотечными модулями, У каждого модуля имеется внешний интерфейс - набор переменных и функций, предназначенных для использования за его пределами. Внутри модуля интерфейс определяется инициализацией некоторых пакетных переменных, с которыми работает стандартный модуль Exporter. За пределами модуля доступ к интерфейсу организуется посредством импортирования имен, что является побочным эффектом команды use. Внешний интерфейс модуля Perl объединяет все, что документировано для всеобщего применения. К недокументированному интерфейсу относится все, что не предназначено для широкой публики. Говоря о модулях в этой главе и о традиционных модулях вообще, мы имеем в виду модули, использующие Exporter. Команды require и use подключают модуль к вашей программе, хотя и оГ). ,, ;:i-ют несколько разной семантикой. Команда require загружает модуль во и; \i/i выполнения с проверкой, позволяющей избежать повторной загрузки модуля. Команда use работает аналогично, но с двумя дополнительными свойствами: загрузкой модуля на стадии компиляции и автоматическим импортированием. Модули, включаемые командой use, обрабатываются на стадии компиляции, а обработка require происходит во время выполнения. Это существенно, поскольку при отсутствии необходимого модуля программа даже не запустится - use не пройдет компиляцию сценария. Другое преимущество use перед require заключается в том, что компилятор получает доступ к прототипам функций в подпрограммах модуля. Прототипы принимаются во внимание только компилятором, но не интерпретатором (впрочем, как говорилось выше, мы рекомендуем пользоваться прототипами только для замены встроенных команд, у которых они имеются). Обработка команды use на стадии компиляции позволяет передавать указания компилятору. Директива (pragma) представляет собой специальный модуль, влияющий на процесс компиляции Perl-кода. Имена директив всегда записываются в нижнем регистре, поэтому при написании обычного модуля следует выбирать имена, начинающиеся с большой буквы. К числу директив, поддерживаемых Perl 5.004, принадлежат autouse, constant, diagnostics, integer, lib, locale, overload, sigtrap, strict, subs и vars. Каждой директиве соответствует отдельная страница руководства. Другое отличие use и require заключается в том, что use выполняет неявное импортирование пакета включаемого модуля. Импортирование функции или переменной из одного пакета в другой создает некое подобие синонима - иначе говоря, появляются два имени, обозначающих одно и то же. Можно провести аналогию с созданием ссылки на файл, находящийся в другом каталоге, командой In / somedir/somefile. После подключения уже не придется вводить полное имя для того, чтобы обратиться к файлу. Аналогично, импортированное имя не приходится уточнять именем пакета (или заранее объявлять с помощью use vars или use subs). Импортированные переменные можно использовать так, словно они являются частью вашего пакета. После импортирования $English:: OUTPUT J\UTOF LUSH в текущий пакет на нее можно ссылаться в виде $OUTPUT_AuTOFLUЫH. . Модули Perl должны иметь расширение .рт. Например, модуль FileHaridle хранится в файле FileHandle.pm. Полный путь к файлу зависит от включаемых путей, хранящихся в глобальном массиве @>INC. В рецепте 12.7 показано, как работать с этим массивом. Если имя модуля содержит одну или несколько последовательностей ::, они преобразуются в разделитель каталогов вашей системы. Следовательно, модуль File::Find в большинстве файловых систем будет храниться в файле File/Find.pm. Например:require "FileKande.pm"; require Filetiandle; use FileHandle; require "Cards/Poker.pm"; require Cards::Poker; use Garde::Poker; # Загрузка во время выполнения # Предполагается ".pm"; # то же, что и выше # Загрузка во время компиляции # Загрузка во время выполнения # Предполагается ".рm"; # то ine, что и выше # Загрузка во время компиляции Правила импортирования/экспортирования Процесс экспортирования демонстрируется ниже на примере гипотетического мод\ ля Cards::Poker. Программа хранится в файле Poker.pm в каталоге Cards, то есть Cards/ Poker.pm (о том, где должен находиться каталог Cards, рассказано в рецепте 12.7). Приведем содержимое этого файла с пронумерованными для удобства строками: 1 package Cards::Poker; 2 use Exporter: 3 @ISA = ('Exporter'); 4 @EXPORT = qw(&shuffle @card_deck); 5 @card_deck = (); # Инициализировать глобальные # переменные пакета 6 sub shuffle { } # Определение # заполняется позднее 7 1; #Не забудьте! В строке 1 объявляется пакет, в который модуль поместит свои глобальные неременные и функции. Обычно модуль начинается с переключения на конкретный пакет, что позволяет ему хранить глобальные переменные и функции так, чтобы они не конфликтовали с переменными и функциями других программ. Имя пакета должно быть записано точно так же, как и при загрузке модуля соответствующей командой use. Не пишите'package Poker только потому, что модуль хранится в файле Poker.pm'. Используйте package Cards:: Poker, поскольку в пользовательской программе будет стоять команда use Cards:: Poke r. Эту распространенную ошибку трудно обнаружить. Если между командами package и use нет точного соответствия, проблемы возникнут лишь при попытке вызвать импортированную функцию или обратиться к импортированной переменной - те будут загадочным образом отсутствовать. Строка 2 загружает модуль Exporter, управляющий внешним интерфейсом модуля (см. ниже). Строка 3 инициализирует специальный, существующий на уровне пакета массив @ISA строкой "Exporter". Когда в программе пользователя встречается команда use Cards: : Poker, Perl неявно вызывает специальный метод, Cards: : Poker->import(). В пакете нет метода import, но это нормально -такой метод есть в пакете Exporter, и вы наследуете его благодаря присваиванию @ISA (ISA = "is а", то есть "является"). Perl обращается, к массиву @ISA пакета при обращении к неопределенному методу. Наследование рассматривается в главе 13 "Классы, объекты и связи". Пока не обращайте на него внимания, но не забывайте вставлять код строк 2 и 3 в каждый новый модуль. Строка 4 заносит список ('&shuffle', '@card_deck') в специальный, существующий на уровне пакета массив ©EXPORT. При импортировании модуля для переменных и функций, перечисленных в этом массиве, создаются синонимы в вызывающем пакете. Благодаря этому после импортирования вам не придется вызывать функцию в виде Poker: :Deck: :shuffle(23) - хватит простого shuffle(23). .^того не произойдет при загрузке Cards::Poker командой require Cards: :Poke': импортирование выполняется только для use. Строки 5 и 6 готовят глобальные переменные и функгщи пакета к экспорч про-ванию (конечно, вы предоставите более конкретные инициализации и onpe.ii.ie- ния, чем в нашем примере). Добавьте другие переменные и функции, включая и те, которые не были включены в внешний интерфейс посредством @EXPORT. Об использовании модуля Exporter рассказано в рецепте 12.1. Наконец, строка 7 определяет общее возвращаемое значение модуля. В нашем случае это просто 1. Если последнее вычисляемое выражение модуля не дает истинного значения, инициируется исключение. Обработка исключений рассматривается в рецепте 12.2. Подойдет любое истинное выражение, будь то 6.02е23 или "Because tchrist and gnat told us to put this here"; однако 1 - каноническая истинная величина, используемая почти во всех модулях. Пакеты обеспечивают группировку и организацию глобальных идентификаторов. Они не имеют ничего общего с ограничением доступа. Код, откомпилированный в пакете Church, может свободно просматривать и изменять переменные пакета State. Пакетные переменные всегда являются глобальными и общедоступными. Но это вполне нормально, поскольку модуль представляет собой больше, чем простой пакет; он также является файлом, а файлы обладают собственной областью действия. Следовательно, если вам нужно ограничить доступ, используйте лексические переменные вместо глобальных. Эта тема рассматривается в рецепте 12.4. Другие типы библиотечных файлов Библиотека представляет собой набор неформально взаимосвязанных функций, используемых другими программами. Библиотеки не обладают жесткой семантикой модулей Perl. Их можно узнать по расширению файла .pi - например, syslog.pl и chat2.pl. Библиотека Perl (а в сущности, любой файл, содержащий код Perl) может загружаться командой do 'file.pl' или require ' f 11, pi'. Второй вариант лучше, поскольку в отличие от do require выполняет неявную проверку ошибок. Команда инициирует исключение, если файл не будет найден в пути @INC, не компилируется или не возвращает истинного значения при выполнении инициализирующего кода (последняя строка с 1, о которой говорилось выше). Другое преимущество require заключается в том, что команда следит за загруженными файлами с помощью глобального хэша %1МС. Если %1МС сообщает, что файл уже был загружен, он не загружается повторно. Библиотеки хорошо работают в программах, однако в ситуациях, когда одна библиотека использует другую, могут возникнуть проблемы. Соответственно, простые библиотеки Perl в значительной степени устарели и были заменены более современными модулями. Однако некоторые программы продолжают пользоваться библиотеками, обычно загружая их командой require вместо do. В Perl встречаются и другие расширения файлов. Расширение .ph используется для заголовочных файлов С, преобразованных в библиотеки Perl утилитой oi2ph (см. рецепт 12.14). Расширение л'з соответствует исходному файлу С (возможно, созданному утилитой h2xs), скомпилированному утилитой xsubpp и компилятором С в машинный код. Процесс создания смешанных модулей рассматривается в рецепте 12.15. До настоящего времени мы рассматривали лишь традиционные модули, которые экспортируют свой интерфейс, предоставляя вызывающей стороне прямой oступ к некоторым подпрограммам и переменным. К этой категории относится большинство модулей. Но некоторые задачи - и некоторые программисты - связываются с хитроумными модулями, содержащими объекты. Объектно-ориентированный модуль редко использует механизм импортирования/экспортирования. Вместо этого он предоставляет объектно-ориентированный интерфейс с конструкторами, деструкторами, методами, наследованием и перегрузкой операторов. Данная тема рассматривается в главе 13. Пользуйтесь готовыми решениями CPAN (Comprehensive Perl Archive Network) представляет собой гигантское хранилище практически всех ресурсов, относящихся к Perl, - исходные тексты, документацию, версии для альтернативных платформ и, что самое главное, модули. Перед тем как браться за новый модуль, загляните на CPAN и поищите там готовое решение. Даже если его не существует, может найтись что-нибудь похожее, полезное в вашей работе. На CPAN можно обратиться по адресу http://www.perl.com/CPAN/CPAN.html(или ftp://www.perl.com/pub/perl/CPAN/CPAN.html). В этом файле кратко описан каждый модуль, входящий в CPAN. Поскольку файл редактируется вручную, в нем могут отсутствовать описания последних модулей. Необходимую информацию можно получить по адресу CPAN/'RECENJ'или CPAN/RECENT.html. Каталог модулей находится по адресу CPAN/modules. В нем содержатся индек-сы всех зарегистрированных модулей, а также имеются три удобных подкаталога: by_module (сортировка по модулям), by_author (сортировка по авторам) и by_category (сортировка по категориям). В каждом подкаталоге перечислены одни и те же модули, но подкаталог by_category, вероятно, наиболее удобен. Находящиеся в нем подкаталоги соответствуют конкретным прикладным областям, среди которых - интерфейсы операционной системы, сетевые взаимодействия, модемы и межпроцессные коммуникации, интерфейсы баз данных, пользова-| ельские интерфейсы, интерфейсы к другим языкам программирования, аутентификация, безопасность и шифрование, World Wide Web, HTML, HTTP, CGI и MIME, графика, операции с растровыми изображениями, построение графиков -и это лишь малая часть. > Смотри также --------------------------------
12.1. Определение интерфейса модуляПроблемаТребуется определить внешний интерфейс модуля с помощью стандартного модуля 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, ищет в вашем пакете глобальные переменные, управляющие его работой. Поскольку они должны быть пакетными, мы используем директиву use vars, чтобы избежать проблем с use strict. Это следующие переменные. $VERSIONПри загрузке модуля можно указать минимальный допустимый номер версии. ели версия окажется ниже, use инициирует исключение. use YourModule 1.86 # Если $VERSION < 1.86, происходит исключение $EXPORT Массив содержит список функций и переменных, экспортируемых в пространство
имен вызывающей стороны, чтобы в дальнейшем к ним можно было обращаться
без уточнения имени пакета. Обычно используется список в форме
> Смотри также -------------------------------
12.2. Обработка ошибок require и useПроблемаЗагружаемый модуль может отсутствовать в системе. Обычно это приводит к фатальной ошибке. Вы хотите обнаружить и перехватить эту ошибку.РешениеПоместите 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). 12.3. Отложенное использование модуляПроблемаНеобходимо организовать загрузку модуля на определенной стадии 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 "Числа" загружает библиотеку вычислений с повышенной точностью лишь при вызове с флагом -Ь. Команда use в данном случае бессмысленна, поскольку она обрабатывается во время компиляции, задолго до проверки условия if. По этой причине мы используем команду require: 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". Если это простое слово, : : преобразуется в разделитель каталогов операционной системы, а в конец добавляется расширение .рт. Но простая переменная интерпретируется как литерал - имя файла. Дело усугубляется тем, что Perl не имеет встроенной функции import. Существует лишь метод класса import, который мы пытаемся применить с сомнительным косвенным объектным синтаксисом. Как и в случае с косвенным применением файловых манипуляторов, косвенный объект можно использовать лишь для простой скалярной переменной, простого слова или блока, возвращающего объект. Выражения, а также отдельные элементы массивов или хэшей здесь недопустимы. Усовершенствованный вариант выглядит так: 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. Также помните, что проверка use strict осуществляется во время компи;!;: 1, :.i. Если модуль Fcnti подключается командой use, прототипы модуля Fcnti "' I,T откомпилированы и мы сможем использовать константы без круглых civ' '"к Если использована команда require или вызов use заключен в eval, как этоде.']:1.ги.'ь выше, компилятор не сможет прочитать прототипы, поэтому константы For I не будут использоваться без скобок. За сведениями об особенностях директивы autouse обращайтесь к электрсгшж документации. > Смотри также -------------------------------
12.4. Ограничение доступа к переменным модуляПроблемаТребуется сделать переменную или функцию закрытой (то есть разрепг, к' использование только в границах пакета).РешениеОбщего решения не существует. Однако можно ограничить доступ на уровне файла, в котором находится модуль, - обычно этого достаточно.КомментарийПомните, что пакет всего лишь определяет способ группировки переменных и функции и потому не поддерживает ограничения доступа. Все содержимое пакета по определению является глобальным и доступным отовсюду. Пакеты лишь группируют, ничего не скрывая. Ограничение доступа возможно только с применением лексических переменных. Предположим, модуль реализован в виде файла 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. Считайте, что глобальные и лексические переменные существуют в разных изменениях, никак не связанных друг с другом. Пакетные команды не имею i ничего общего с лексическими переменными. После установки текущего пре фикса первая глобальная переменная $х в действительности представляет собой $Alpha: : х, а вторая - $Beta: : х, поскольку промежуточная команда package измени-;ia префикс по умолчанию. Доступ к пакетным идентификаторам при указании полного имени может осуществляться откуда угодно, как это делается в команде print. Итак, пакеты не позволяют ограничивать доступ - зато на это способны моду-.'щ, поскольку они находятся в файлах, а файл всегда обладает собственной областью действия. Приведенный ниже простой модуль находится в файле Flipper.pni ii экспортирует две функции, flip_words и flip_boundary. Первая функция переставляет слова строки в обратном порядке, а вторая изменяет определение границы слова. # 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 уровня файла. Как говорилось выше, эта переменная ограничивается границами файла, а не пакета. Весь код той же области действия, расположенный после ее объявления, прекрасно видит $Separatrix. Хотя глобальные переменные не экспортировались, к ним можно обращаться по полному имени - например, $Flipper: : VERSION. Лексические переменные, существующие в некоторой области действия, нельзя прочитать или изменить вне этой области, которая в данном случае соответствует всему файлу после объявления переменной. На лексические переменные нельзя ссылаться по полному имени или экспортировать их; экспортирование возможно лишь для глобальных переменных. Если кому-либо за пределами модуля потребуется просмотреть или изменить лексические переменные файла, они должны обратиться с запросом к модулю. Именно здесь в игру вступает функция flip_boundary, обеспечивающая косвенный доступ к закрытым компонентам МОДУЛЯ. Работа приведенного выше модуля ничуть не изменилась бы, будь $Separa:.' ix пакетной глобальной переменной, а не файловой лексической. Теоретически к ней можно было бы обратиться снаружи так, что модулю об этом ничего не было известно. С другой стороны, не стоит увлекаться чрезмерными ограничениями и щедро уснащать модули лексическими переменными с файловой областью действия. У вас уже имеется пространство имен (в нашем примере - Flipper), в котором можно сохранить все необходимые идентификаторы. Собственно, для этого оно и предназначено. Хороший стиль программирования на Perl почти всегда избегает полностью уточненных идентификаторов. Если уж речь зашла о стиле, регистр символов в идентификаторах модуля Flipper выбирался не случайно. В соответствии с руководством по стилю программирования на Perl. символами верхнего регистра записываются идентификаторы, имеющие специальное значение для Perl. Имена функций и локальных неременных записываются в нижнем регистре. Устойчивые переменные модуля (файловые лексические или пакетные глобальные) начинаются с символа верхней регистра. Если идентификатор состоит из нескольких слов, то для удобства чтения эти слова разделяются символами подчеркивания. Пожалуйста, не разделяйте слова символами верхнего регистра без подчеркиваний - в конце концов, вряд ли вам захотелось бы читать эту книгу без пробелов. > Смотри также -------------------------------
12.5. Определение пакета вызывающей стороныПроблемаТребуется узнать текущий или вызывающий пакет.РешениеТекущий пакет определяется так:$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". > Смотри также -----------------------------
12.6. Автоматизированное выполнение завершающего кодаПроблемаТребуется создать для модуля начальный и завершающий код, вызываемый автоматически без вмешательства пользователя.РешениеНачальный код реализуется просто - разместите нужные команды вне определений подпрограмм в файле модуля. Завершающий код помещается в блок 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: $! Первая часть кода, не входящая в объявления функций, выполняется во время загрузки модуля. Для этого от пользователя модуля не потребуется никаких специальных действий. Впрочем, для кого-нибудь это может оказаться неприятным сюрпризом, поскольку при недоступности журнала die вызовет сбой при выполнении use или require. Блоки END не отличаются от других функций завершения - trap 0 в команд ном интерпретаторе, atexit в языке С или глобальные деструкторы в объектно-ориентированных языках. Порядок выполнения END противоположен порядку загрузки модулей; иначе говоря, первым выполняется блок END последнего загруженного модуля. Завершающий код вызывается независимо от причины завершения - нормального достижения конца основной программы, непосредственного вызова функции exit или необработанного исключения (например. die или ошибки деления на ноль). Однако с неперехваченными сигналами дело обстоит иначе. При завершении по сигналу блоки завершения не вызываются. Проблема решается следующей директивой: use sigtrap qw(die normal-signals error-signals) END также не вызывается в случае, если процесс вызывает функцию ехес, поскольку процесс остается тем же самым, изменяется лишь программа. Все стандартные атрибуты (идентификатор процесса и его родителя, идентификаторы пользователя и группы, маска доступа, текущий каталог, переменные окружения, ограничения ресурсов и накопленная статистика), открытые файловые дескрипторы (однако см. описание переменной $Т в perlvar(l)) сохраняются. Другой подход привел бы к лишним вызовам блоков завершения в программах с ручной обработкой fork и ехес. Это было бы нежелательно. > Смотри также ------------------------------
|