Журнал LinuxFormat - перейти на главную

LXF72:Perl

Материал из Linuxformat
Перейти к: навигация, поиск

Содержание

Perl Функции и модули

ЧАсТь 4 Не все сценарии надо писать самостоятельно с первой до последней строчки. Марко Фиоретти (Marco ‘Prefab’ Fioretti) показывает, как можно использовать код повторно.

я надеюсь использовать последнюю статью из этой серии на всю катушку, продемонстрировав вам некоторые мощные возможности Perl. Мы начнём с с функций, затем рассмотрим модули, а в конце я покажу как использовать их для доступа к базам данных. итак, поехали!

Как в любом хорошем языке программирования, в Perl повторно используемые кусочки кода можно оформить в виде функций (тут они называются подпрограммами-subroutines). Вот пример простой подпрограммы:

sub print_array
{
my $COUNTER = 0; # вспомогательный счетчик
foreach $ITEM (@_) {
$COUNTER++;
print «# $COUNTER: $ITEM\n»;
}
$COUNTER;
}
# А теперь используем это:
@FRIENDS = (‘John’, ‘Jill’, ‘Kelly’, ‘Martin’);
$NUMBER = &print_array(@FRIENDS);
print «These are my $NUMBER friends\n»;

Давайте для начала посмотрим на самую простую часть. Как вы видите, подпрограмма начинается с ключевого слова sub, имени (здесь — print_array), за которыми следует код (возможно достаточно сложный), заключённый в фигурные скобки. При вызове процедуры необходимо добавлять пред именем символ &, а сразу после него указывать параметры в круглых скобках.

главный секрет

В данном выше коде есть два неочевидных момента. Это секрет переменной @_, а также тот факт, что я «присвоил» переменной $NUMBER подпрограмму. на самом деле в @_ нет ничего особенного, это просто массив, в который Perl помещает все параметры, полученные при вызове. Присвоение переменной $NUMBER возможно потому, что все подпрограммы Perl возвращают значение последней выполненной инструкции. Возможно, вы скажете что $COUNTER — это просто переменная, а не инструкция. Да, это странно, но в Perl это прекрасно работает.

Процедуры можно комбинировать с хешами (самым мощным типом переменных в Perl) очень интересным, хотя и слегка мистическим способом. Практически во всех реальных ситуациях вы можете использовать процедуры как значения хэша, так что впоследствии к ним можно будет обращаться, используя ключи. Посмотрите на это:

%FUNZ = (
‘L’, {‘D’, ‘LIST’, ‘F’, \&MONTHLY_LIST}, #Список всех звонков этого месяца
‘P’, {‘D’, ‘PLOT_2D’, ‘F’, \&GEN_PLOT }, #нарисовать график всех звонков
‘B’, {‘D’, ‘BACKUP’, ‘F’, \&BACKUP_ALL } #Выполнить резервное копирование
#Еще много таких же элементов пропущено для краткости
);
#Покажем пользователю все варианты
foreach (sort keys %FUNZ) {
printf «%2.2s %-20.20s», $_, $FUNZ{$_}{‘D’}, «\n»;
}
#...и вызовем процедуру, которую он выбрал
if ($USER_INPUT eq ‘B’) { # резервное копирование
my $rsub = $FUNZ{‘B’}{‘F’};
&$rsub((‘All’, ‘the’, ‘arguments’,for, ‘this’, ‘subroutine’));

Это кусочек моего работающего скрипта, который предлагает пользователю выбрать действие, которое нужно выполнить. он кажется таинственным, но на самом деле это не так. %FUNZ — это хэш, ключами которого являются буквы L, P и B, а значениями — неименованные хэши, указанные в виде списков в фигурных скобках. Каждый из этих хэшей имеет ключ D (от слова Description, описание) и F (Function, функция). значением ключа D является простая строка, а значением F — указатель на функцию, именно его обозначает синтаксис «\&». Другими словами, $FUNZ{‘B’}{‘F’} — это указатель на функцию BACKUP_ALL.

При запуске скрипта он выводит на терминал примерно следующее:

L LIST
P PLOT_2D
B BACKUP

Клавиша, которую нажал пользователь, сохраняется в переменной $USER_INPUT. Если была выбрана буква B, последний кусочек кода запишет указатель на BACKUP_ALL в переменную $rsub, и в последней строке вызовет эту процедуру, передав ей все нужные аргументы. Так что код &$rsub обозначает «Выполнить код по адресу, который хранится в переменной $rsub». здорово, правда?

Функции хороши для повторного использования кода, но они могут не всё. обычно они работают только внутри небольшого набора сценариев, порой взаимодействуя (кто-то скажет «смешиваясь») с другим кодом не очевидным образом. например, вы вероятно не сможете запустить упомянутые выше функции в своей системе, не внося в них некоторых изменений. я не стремился сделать их пригодными для использования в любых условиях, так что в них используется много ссылок на глобальные переменные (например $BACKUP_SUBFOLDER), которые могут у вас отсутствовать или иметь другой смысл.

Код, которым можно поделиться

Если вам нужен чёрный ящик, который правильно работает в любой программе без всяких неприятных побочных эффектов, значит вам нужны модули Perl. Эти объекты содержат в заголовке объявление пакета, имя которого должно совпадать с именем файла минус расширение .pm.

package My_Mail_Filter; # объявление пакета
sub filter_incoming_messages {
#Код процедуры
}
sub remove_duplicate_messages {
# Код другой процедуры
}
1;

объявление пакета помещает весь код, который за ним следует, в своё пространство имён. он состоит из простого списка символов (имён процедур, переменных и так далее), которые напрямую доступны внутри модуля. При необходимости пакет может обратиться и к переменным из другого пространства имён, но вам каждый раз придётся указывать его полностью. Таким образом гарантируется, что код модуля не смешается с другими модулями и основным сценарием, хотя при этом вы можете сознательно обратиться к любому внешнему объекту. оператор 1 в конце — это стандартный приём, использующийся для того, чтобы показать что модуль был загружен успешно, поскольку он вернул ненулевое значение.


из своей программы вы можете загрузить модуль и использовать его функции примерно так:

use My_Mail_Filter;
My_Mail_Filter::remove_duplicate_messages($CURRENT_MAILBOX);

Все модули из Comprehensive Perl Archive Network (или CPAN, смотри во врезке «Сетевой банк модулей Perl» справа) поставляются с документацией, достаточной для того, чтобы узнать, какая процедура ожидает каких параметров. интерпретатор Perl ищет модули в каталогах, определённых в системной переменной @INC и во всех их подкаталогах. чтобы получить весь список, просто наберите в командной строке

perl -e ‘print join(«\n»,@INC).»\n»’

использование use.

иногда (как правило, на офисном или школьном компьютере) у вас нет доступа к системным каталогам, перечисленным в переменной @INC, и поэтому вам приходится устанавливать модули куда-нибудь ещё и указывать Perl, откуда их брать. один из самых простых способов — это использование конструкции use lib. Если вы поместили My_Mail_Filter. pm в каталог /home/my_account/.perl_modules/, то вы можете начать сценарий со строк

use lib qw(/home/my_account/.perl_modules/);
use My_Mail_Filter;

Таким образом Perl обнаружит этот модуль.

Многие сценарии добиваются того же результата при помощи require:

require «/home/my_account/.perl_modules/My_Mail_Filter.pm»;

Главное отличие между use и require состоит в том, что require подгружает модуль в уже работающий сценарий, исполняя строку в которой указан этот оператор. Возможно, это то что вам нужно, так как при помощи require можно подгружать или нет тот или иной модуль в зависимости от некоторых условий. но если нужный файл отсутствует или не загружается, то это приведёт к прекращению работы программы, а она уже возможно начала изменение ваших данных…

С другой стороны, ключевое слово use всегда выполняет код модуля в самом начале, до того как начинается выполнение основного сценария (во время компиляции, как говорят программисты), поэтому модуль оказывает влияние на работу всего сценария целиком.

При помощи use мы можем получить доступ к базе данных.

Компьютеры и интернет были созданы для сбора, хранения и поиска информации, и базы данных играют в этом существенную роль. они обычно состоят из нескольких файлов, которые содержат данные в особом формате, и из программы, называемой сервером базы данных, которая читает и пишет информацию в эти файлы. Сервер делает это по запросу от клиентского приложения, которое может быть написано на Perl с использованием модуля доступа к базе данных. Модули позволяют сделать взаимодействие с открытыми базами данных из Perl очень простым. Вот короткий пример, демонстрирующий работу с MySQL.

use strict;
use Mysql;
###################################
###################################
my $DBHOST = «компьютер_на_котором_запущен_сервер_базы_данных»;
my $DBNAME = «имя_базы_данных_к_которой_мы_хотим_обратиться»;
my $DBUSER = «имя_учётной_записи_для_MySql»; # может отличаться от имени учётной записи в Linux
my $DBPASS = «пароль_учётной_записи_для_MySQK»;
my @RESULTS;
###################################
###################################
# Подключаемся к серверу баз данных MySQL
my $DB = Mysql->connect($DBHOST, $DBNAME, $DBUSER, $DBPASS);
# запрашиваем базу данных
my $qry = «SELECT * FROM appdb WHERE P_ID < 100»;
my $res = $DB->query($qry);
while( @RESULTS = $res->fetchrow) {
print «$RESULTS[0], $RESULTS[1], $RESULTS[2]\n»;
}

Этот код работает при условии подключения к базе данных, которая была уже создана и заполнена информацией. Первой интересной командой является та, при помощи которой мы присоединяемся к серверу баз данных MySQL. Поскольку раньше мы потребовали от сценария загрузить модуль Mysql, то теперь нам достаточно написать только Mysql->connect и указать требуемые параметры. Согласитесь, что это здорово! Вне зависимости от того, кто написал и поддерживает модуль MySQL, вы загружаете его и он просто работает.

В следующих двух строчках мы выполняем запрос к базе данных. Сценарий строится на том предположении, что база данных, к которой мы подключаемся, содержит таблицу appdb, в которой есть численное поле P_ID. Первая команда создаёт запрос на языке SQL и сохраняет его в переменной $qry. Это запрос является константной строкой и просто запрашивает все поля (SELECT *) записей из таблицы appdb при условии, что поле P_ID для них меньше 100. Вы можете генерировать запрос к базе на лету, например включая текущие значения переменных в строку $qry.

В следующей строчке сценария запрос выполняется и результат связывается с переменной $res. через неё сценарий может получить доступ ко всем строчкам полученного набора данных. Вставка новой записи в таблицу базы делается точно таким же способом, только SQL-запрос потребуется другой.

Вы уже обратили внимание на цикл while в самом конце? Код $res->fetchrow применяет процедуру (или «метод») fetchrow модуля MySQL к переменной $res. В результате при каждом таком вызове возвращается содержимое следующей строки из полученного набора данных, хранящегося в переменной $res. При этом каждое поле записи записывается в правильном порядке в массив $RESULT. Вот почему оператор print внутри цикла может просто выводить на терминал содержимое полученных полей.

я надеюсь, что эта короткая серия статей дала вам достаточно, чтобы начать работать (и в удовольствие!) с огромными возможностями Perl. настало время писать свои собственные программы. Удачи вам!

Персональные инструменты
купить
подписаться
Яндекс.Метрика