Справочник по Perl: Функции, относящиеся к области видимости переменных. WDH: PERL - Встроенные функции. «Старые» современные возможности Perl

Встроенные функции используются как термы выражений и подразделяются на две категории: списковые операторы и унарные операторы. Это влияет на их приоритет по отношению к оператору "," – запятая. Списковые операторы могут именть множество (список) аргументов, а унарные только один. Таким образом запятая завершает аргументы унарного оператора и разделяет аргументы спискового.

Аргумент унарного оператора воспринимается обычно в скалярном контексте а спискового как в скалярном так и

списковом причем скалярные аргументы идут первыми. В дальнешем списковые аргументы мы будем обозначать словом "LIST" это значит что функция имеет список аргументов разделенных запятой.

Аргументы функций можно заключать в круглые скобки и таким образом обозначать что «это функция» и приоритет не имеет значения иначе это списковый или унарный оператор с определенным фиксированным приоритетом. Пробел после имени функции и скобкой значения не имеет. Поэтому будьте внимательны!

Пример:

print 1 + 2 + 3; # результат 6

print(1+2)+3; # результат 3

print (1+2)+3; # опять 3

print (1+2+3); # 6

Если функция возвращает результат как в скалярном так и в списковом контексте то код выхода по ошибке – скаляр c неопределенным значением или пустой список.

Запомните правило:

Не существует общего правила преобразования списка в скаляр!

Каждый оператор и функция имеют свой вид значения в скалярном котексте.

Для одних это количество элементов из скалярного контекста. Для других первый элемент списка или последний или количество успешных операций. Каждый свое если вы специально не указываете.


Оператор "-X".

-X указатель файла

-X выражение

Проверка файла, где "X" одно из ниже описанных значений.

Это унарный оператор с одним аргументом – либо именем файла либо указателем файла. Проверяет одно из условий. Если аргумент не указан то берется значение переменной $_. Для ключа -t STDIN.

Результат 1 если true и "" если false или неопределенное значение если файл не найден. Несмотря на странный вид это унарный оператор с соответсвующим приоритетом. Аргумент можно заключать в круглые скобки.

"X " имеет следующие значения:

-r Файл разрешен на чтение эффективным uid/gid

-w на запись -//-

-x исполнение -//-

-o принадлежит эффективному uid (идентификатор пользователя)

-R Файл разрешен на чтение реальным uid/gid

-W на запись -//-

-X исполнение -//-

-O принадлежит реальному uid

-e файл существует

-z пустой

-s не пустой

-f обычный текст

-d директория

-p pipes (конвейер)

-S socket (гнездо)

-b специальное блочное устройство

-c -//– символьное -//-

-t указатель на уст-во tty

-u установлен бит setuid

-g -//– setgid

-k -//– sticky

-T текстовой файл

-B двоичный

-M «возраст» файла в днях на момент старта скрипта

-A дней с последнего чтения

-C дней с последней модификации inode


abs выражение

Абсолютное значение выражения


accept NEWSOCKET, GENERICSOCKET

Окрыть новый сокет по внешнему запросу. Аналогично системному вызову accept() . Возвращает адрес или false в случае неудачи.


alarm секунды

Послать сигнал SIGALARM текущиму процессу по истечении указанного промежутка времени. Не допустимо делать несколько вызовов одновременно в одном промежутке времени.

Возвращает остаток времени предыдущего счетчика.


atan2 Y, X

Арктангес отношения Y к X в пределах от -pi до +pi.


bind сокит, имя

Назначить сетевой адрес сокиту. Подобно системному вызову bind . Возвращает true в случае успеха и false иначе.

Имя – пакетный адрес сокита.


binmode файл

Открыть файл для двоичного (binary) режима доступа.

В обычном режиме при вводе символы CR LF транслируются в LF, а при выводе LF транслируется в CR LF.


функция bless

Данная функция определяет что объект указанный ссылкой принадлежит классу. Если класс не указан то он относится к текущему классу. Обычно это последний оператор в конструкторе объекта.

Необходимо обязательно указывать имя класса если определяемый объект может наследоваться классом-потомком.


функция caller

caller выражение

Возвращает контекст вызова текущей подпрограммы.

В скалярном контексте true если это подпрограмма или процедура вызванная операторами eval() или require() и false – иначе.

В списковом это список ($package, $filename, $line)

$package – имя пакета

$filename – имя файла пакета

$line – номер строки откуда был сделан вызов.

Если указано выражение то возвращается список:

($package, $filename, $line, $subroutine, $hasargs, $wantargs )

Выражение определяет «глубину» вложенности просмотра стека вызовов.

$subroutine – имя подпрограммы

$hasargs – имеющиеся аргументы

$wantargs – необходимые аргументы

Применение данной функции в DB пакете возвращает более детальную информацию. К списку аргументов добавляется список @DB::args.


chdir выражение

Перейти в директорию указанную выражением. Если выражение отсутсвует то перейти в «домашнию» директорию.

Возвращает true в случае успеха и false – неудачи.


chmod список

Изменить права доступа к файлам указанным в списке.

Первый элемент в списке – числовое,обычно восьмеричное значение прав.

Возвращает количество файлов которым были изменены права.

chmod 0666 "f1", "f2", "f3";


chomp переменная

chomp список

Удаляет в конце строки символ указанный переменной $/.

Обычно это "LF". Возвращает количество удаленных символов. Если переменная не указана то используется переменная $_.

Если указан список то обрабатывается каждая строка списка и возвращается общее количество удаленных символов.


chop переменная

chop список

Полностью аналогично функции chomp но всегда удаляет последний символ строки.


chown список

Изменить «владельца» и «группу» файлов списка.

Первый элемент – номер нового владельца, второй номер новой группы а далее список файлов.

В большинстве Юникс-систем вы не сможете сменить владельца если вы не суперпользователь, но можете изменить группу если это одна из ваших «вторичных» групп.


chr число

Возвращает символ с указанным кодом кодировки.


chroot директория

Сделать «корневой» указанную директорию.

«Корневая» – значит внутри программы на нее можно ссылаться как "/ ".


close файл

Закрыть открытый файл или поток. Можно не использовать

если один и тот же файл открывается повтортно т.к. он будет автоматически закрываться новым вызовом open().


closedir директория

Закрыть директорию открытую функцией opendir() .


connect сокит, имя

Связаться с удаленным сокитом. Данная функция подобна системному вызову connect() . Возвращает true в случае успеха

и false при ошибке.


cos выражение

Косинус выражения в радианах.


crypt текст, salt

Шифрация текта. Аналогично системному crypt() .

dbmclose хеш

Закрывает связь между хеш и базой данных.


dbmopen хеш, база, режим

Ассоциировать указанный хеш и базу в определенном режиме.

Дело в том что в Перл реализован очень удобный механизм работы с базами данных типа dbm, ndbm, sbdm, gdbm и Berkeley DB. Для этого нужно связать (открыть) базу под именем хеша. Вся работа с записями базы выполняется так будто это обычный хеш (ассоциативный массив).

Более подробно смотрите описание функции AnyDBM() .


define выражение

Возвращает true если значение определено (существует) и false если нет. Многие операции завершаются не определенным значением, например чтение после конца файла, использование не определенных переменных, системные ошибки и т.д.

Данная функция позволяет различать нулевое и не определенные значения, определенность или не определенность функций или ссылок. При использовании определенности ключа в хеше она возвращает определен данный ключ или нет но не существует он или нет.


delete выражение

Удалить значение из хеша. Возвращает удаляемое значение или не определенность если ни чего не удаляется.

Удаление из массива окружения $ENV{} – изменяет окружение.

Удаление из хеша связанного с базой данных – удаляет запись в базе данных.


die список

Если программа не была запущена из eval() данная функция выводит список в поток STDERR и завершает работу программы с кодом из переменной $!. Если $! содержит 0 то с кодом ($? >> 8). Если и это 0 то с кодом 255.

При вызове из eval() сообщение об ошибке заносится в переменную $@ и eval() завершается с неопределенным значением. Это позволяет отробатывать ошибочные ситуации не завершая головной модуль.

Если строка из списка не завершается символом "\n" то дополнительно печатается номер строки скрипта откуда вызвана функция и номер входной строки исли таковой имеется.


do BLOCK

Это не совсем функция. Здесь возвращается значение последнего оператора блока. Когда используется циклический модификатор

BLOCK исполняется перед проверкой условия цикла в отличии от обычных циклов.


do подпрограмма

Устаревший метод вызова подпрограмм.


do выражение

Значение выражения воспринимается как имя файла Перл скрипта.

Изначально это применялось для включения подпрограмм библиотеки.

Сейчас правильней использовать вызовы use() и require() которые обеспечивают контроль ошибок и завершения.


dump метка

Выполнние «дампа» текущей памяти. Применялось для возможности исрользовать программу undump что бы включить текущий исполняемый код в программу после инициализации всех переменных.

Выполнение этой новой программы будет начинаться оператором goto метка. Если метка опущена то запуск сначала.

Внимание! Файлы открытые до момента дампа не будут открытыми

в момент исполнения новой программой.


each хеш

Возвращает 2-элементный массив. Первый элемент – ключ второй – значение следующего элемента хеша. Позволяет «просматривать» все значения хеша в определенном порядке. Сброс переменной цикла происходит только после прохождения всего массива.


eof()

Возвращает 1 если выполняется чтение после конца или чтение из не открытого файла. Аргументом должно быть выражение возвращающее существующий указатель файла. Данную функцию нельзя использовать для чтения файлов открытых как терминалы.

Если аргумент не указан то используется последний читаемый файл.

Использование в форме eof() целесообразно применять в циклах while(<>) для определения конца только последнего файла из

списка. Применяйте eof(ARGV) или eof для определения конца каждого файла из списка.


eval выражение

eval выражение

Выражение сканируется и выполняется как обычная Перл программа.

Это делается в контексте текущей программы поэтому можно использовать уже определенные переменные и подпрограммы.

Ворзвращается результат последнего оператора в блоке или значение оператора return . Если обнаружится синтаксическая ошибка или выполнится оператор die возвращается не определенное значение а переменная $@ содержит сообщение об ошибке. Если ошибки не было то $@ содержит нулевую строку. При отсутсвии аргумента берется значение переменной $_.


exec список

Выполняется системная команда из списка и выполнение программы завершается. Если вам необходимо вернуться то лучше применяйте функцию system() . Если в списке количество аргументов больше 1 или список это массив с количеством элементов больше 1 то выполняется системный вызов execvp() с аргументами из списка.

Если один скалярный аргумент то он сканируется на обработку метасимволов и если они присутсвуют то полученные аргуметы передаются интерпретатору /bin/sh -c для дальнейшей обработки.

Если метасимволов нет то аргумент расщепляется на слова и передается системному вызову execvp() что более эффективно.

Внимание! exec() и system(0) не сбрасывают буфер ввода/вывода поэтому необходимо установить переменную $| для предотвращения потери выходной информации.


exists выражение

Возвращает true если существует указанный ключ хеша даже если не определено его значение.


exit выражение

Выполнение программы завершается с кодом выражения.


exp выражение

Возвращает e (основание натурального логарифма) в степени выражения.


fcntl файл, функция, скаляр

Выполняет системный вызов fcntl(). Для использования обязательно выполнить use Fcntl;


fileno файл

Возвращает описатель файла для данного указателя файла.


flock файл, операция

Системный вызов flock()


fork

Системный вызов fork(). Возвращает pid потомка для родителя и 0 для потомка в случае успеха иначе – неопределенное значение.


format

Декларативная функция Перл. Определяет формат вывода для оператора write . Подробно смотрите главу Форматы.


formline формат, список

Внутренняя функция используемая в format. Форматирует вывод параметров из списка. Результат помещается в переменную $^A.Функция write просто выводит значение этой переменной, но ее так же можно читать а затем обнулять. Обычно format выполняе один вызов formline для одной строки формы, но formline не разделяет строки формата. Это значит что символы "~" и "~~" рассматривают весь формат как одну строку поэтому необходимо использовать многострочные формыд для описания одной записи формата.


getc файл

getc файл

Читает и возвращает символ из указанного файла.

Если файл не указан то читается STDIN. После конца файла возвращает пустую строку.


getlogin

Возвращает текущий login из файла /etc/utmp

Не применяйте для идентификации пользователя он не так сильно «закрыт» как getpwuid().


getpeername сокит

Возвращает упакованный адрес удаленного сокита.


getpgrp PID

Возвращает группу процесса с указанным PID. Если аргумент отсутсвует возвращает группу текущего процесса.


getppid

Возвращает PID родительского процесса.


getpriority WHICH, WHO

Возвращает текущий приоритет процесса, группы процессов или пользователя.

11.1. Определение подпрограммы

Подпрограмма может быть определена в любом месте основной программы при помощи описания

Sub name [(proto)] ({block}};

Здесь name имя подпрограммы;

(proto) прототип, конструкция, используемая для описания передаваемых подпрограмме параметров;

{block} блок операторов, являющийся определением подпрограммы и выполняющийся при каждом ее вызове.

Форма

Sub name [ (proto)];

Представляет собой предварительное объявление подпрограммы без ее определения. Пользователь, предпочитающий помещать описания всех подпрограмм в конце основной программы, должен при вызове еще не определенной фуНКЦИИ ИСПОЛЬЗОВаТЬ Специальный СИНТаКСИС &лагае ИЛИ name (см.раздел 11.2). Если же некоторое имя предварительно объявить в качестве имени функции, то сразу после объявления к этой функции можно обращаться просто по имени без применения специального синтаксиса.

#!/usr/bin/perl sub max {

My $maximum = shift @$_;

my $x;

foreach $x (@_) {

$maximum=$x if ($x > $maximum) ;

}

return $maximum } print "Наибольший аргумент=", max(3,5,17,9), "\n";

В данном примере функция max () возвращает наибольший из своих аргументов. Об использовании функции ту() и массива @_ будет рассказано ниже.

Данный способ определения подпрограмм не является единственным. Существуют и другие варианты:

  • текст подпрограммы может храниться в отдельном файле и загружаться в основную программу при ПОМОЩИ КЛЮЧеВЫХ СЛОВ do, require, use;

П строка, содержащая текст подпрограммы, может быть передана в качестве аргумента функции eval о (см. раздел 10.3); в этом случае компиляция кода подпрограммы осуществляется при каждом вызове функции eval ();

  • анонимную подпрограмму можно определить при помощи ссылки на нее (см. раздел 9.2.4. Т).

Конструкция do filename вызывает выполнение Peri-программы, содержащейся в файле filename. Если файл filename недоступен для чтения, функ-ция do возвращает неопределенное значение и присваивает соответствующее значение специальной переменной $!. Если файл filename может быть прочитан, но возникают ошибки при его компиляции или выполнении, то функция do возвращает неопределенное значение и помещает в переменную $@ сообщение с указанием строки, содержащей ошибку. Если компиляция прошла успешно, функция do возвращает значение последнего выражения,

Вычисленного В файле filename.

Специальная переменная $@ "используется для хранения сообщения, генерируемого при последнем обращении к функциям eval () или do filename,

# файл "l.pl":

#!/usr/bin/perl \ do "2.pi"; \ print "ошибка: $@\n" if $@; do "3.pl"; " . j print "системная ошибка: $!\n" if $!;

# файл "2.pi":

$x=l;

$y=0;

$z=$x/$y;

print "z= $z\n";

Peri-программа "i.pi", используя конструкцию do filename, пытается выполнить сценарии, содержащиеся в файлах "2.pi" и "З.р1". Первый из них содержит в третьей строке операцию деления на 0, вызывающую появление ошибки во время выполнения программы, а второй вообще не существует. В результате выполнения файла "i .pi" будут выведены следующие сообщения:

ошибка: Illegal division by zero at 2.pi line 3. системная ошибка: No such file or directory

Ключевые слова use и require используются для включения в текущую программу подпрограмм из других модулей.

(Директивы компилятора use и require рассмотрены в главе 12.)

11.2. Вызов подпрограммы

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

Sname args; Sname(args); Sname;

Здесь args обозначает список аргументов подпрограммы. Если список аргументов отсутствует, вместо него используется специальный массив @_.

Если после имени подпрограммы следуют скобки, префикс & можно опустить:

Name (args); name();

Если до обращения к ней подпрограмма была объявлена или импортирована, то скобки также можно опустить:

sub name {. . .}; name args;

name ;

Если подпрограмма вызывается через ссылку на нее, префикс является обязательным:

$subref = sub (...}; Ssubref(args) ;

ssubref;

Подпрограмма может быть использована в выражении как функция, возвращающая значение. По умолчанию значением подпрограммы является последнее вычисленное в ней выражение. Его можно изменить, указав явно в качестве аргумента функцию return () в любой точке подпрограммы. Возвращаемое значение может быть скалярной величиной или массивом.

11.3. Локальные переменные в подпрограммах

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

(Пакеты рассматриваются в главе 12, а специальные переменные - в главе 14.)

Таким образом, переменная, впервые встретившаяся- в некоторой подпрограмме, становится доступной во всем пакете, к которому эта подпрограмма принадлежит. Любая переменная в Perl по умолчанию считается глобальной, но эта глобальность ограничена рамками пакета. Иногда бывает необходимо ограничить область действия переменной рамками подпрограммы или блока, в которых она определена. Такие переменные называются локальными. В языке Perl существуют два способа описания локальных переменных: при

ПОМОЩИ ФУНКЦИЙ ту О И local () .

11.3.1. Функция ту()

Функция ту о используется для объявления одной или нескольких переменных локальными:

My EXPR

И ограничивает их область действия:

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

Если выражение EXPR содержит список переменных, то он должен быть заключен в скобки:

my ($myvar, @mylist, %myhash);

Одновременно с объявлением переменные могут быть инициализированы:

My $pi = 3.14159;

Ту ($pi, $exp) = (3.14159, 2.71828);

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

11.3.2. Функция /оса/0

Функция local о также используется для объявления и инициализации переменных:

local EXPR;

local ($myvar, @mylist, %myhash);

Local $pi = 3.14159;

local ($pi, $exp) = (3.14159, 2.71828);

Но, в отличие от функции ту о она создает не локальные переменные, а временные значения для глобальных переменных внутри:

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

В зависимости от того, в каком месте вызвана для объявления переменных сама функция local (). Если функция local () применяется для описания нескольких переменных, они должны быть заключены в скобки. Если глобальная переменная, объявленная при помощи этой функции, ранее встречалась до объявления и имела некоторое значение, то это значение сохраняется в скрытом стеке и восстанавливается после выхода соответственно из подпрограммы, блока, функции eval (} или файла. Переменная, объявленная при помощи функции local (), или, точнее, ее временное значение, доступна для" любой функции, вызванной внутри подпрограммы, блока, функции eval о или файла, в которых сделано объявление. Такую переменную называют динамической, а ее область видимости - динамической областью видимости. В названии отражается тот факт, что область видимости переменной динамически изменяется с каждым вызовом функции, получающей доступ к этой переменной.

Функция ту о является относительно новой, она появилась в версии Perl 5. Для создания действительно локальных переменных рекомендуется использовать именно функцию ту о, а не функцию local о. Впрочем, есть несколько исключений. О них мы расскажем ниже.

В следующем примере показано, чем отличаются переменные, объявленные

При ПОМОЩИ фуНКЦИЙ ту () И local () .

Sub fl{

Local ($x) = "aaaa";

My($y) = "bbbb";

Print("fl: x = $x\n");

Print("fl: y="$y\n\n");

F 2 (} ;

Print("fl: x = $x\n");

Print("fl: у = $y\n\n");

} " " " sub f2{

Print("f2: x = $x\n");

Print("f2: y=$y\n\n");

$x = "cccc";

$y = "dddd";

Print("f2: x = $x\n");

Print("f2: y=$y\n\n");

I

Ре |\льтатом выполнения данного примера будет следующий вывод:

II л = aaaa

F. у = bbbb ,

2: x - aaaa с2: у =

f2: x = cccc £2: у = dddd

Fl: x = cccc fl: у = bbbb

Как видно из приведенного результата, функция f 2 () не имеет доступа к переменной $у, объявленной при помощи функции ту о внутри функции fi О, и, напротив, имеет доступ к переменной $х, объявленной внутри fl о

при ПОМОЩИ фуНКЦИИ local () .

11.4. Передача параметров

Информация в подпрограмму и обратно передается через параметры (аргументы). Для передачи параметров в подпрограмму используется специальный массив @_. Все параметры запоминаются в элементах массива $_ [ о ], $_ [ 1 ] и т. д. Такой механизм позволяет передавать в подпрограмму произвольное количество параметров.

Массив @_ является локальным для данной подпрограммы, но его элементы - это псевдонимы действительных скалярных параметров. Изменение элемента массива @_ вызывает изменение соответствующего действительного параметра.

В языках программирования различают передачу параметров по ссылке и по значению. При передаче параметров по значению подпрограмма получает копию переменной. Изменение копии внутри подпрограммы не влияет на ее оригинал. При передаче параметров по ссылке подпрограмма получает доступ к самой переменной и может ее изменять.

Передача параметров через специальный массив @_ фактически является передачей параметров по ссылке. В языке Perl можно реализовать передачу параметров по значению, если внутри подпрограммы при помощи функции ту о объявить локальные переменные и присвоить им значения фактических параметров из массива @_, как это сделано в следующем примере.

#!/usr/bin/perl

# Передача в подпрограмму параметров по значению sub f {

my($x, $y) = @_; return (++$х * -$у); }

$val = f ^lib-print "Значение (9+1) * (11-1) равно $val.\n"; $х = 9; $У = 11;

$val = f($x,$y);

print "Значение ($х+1) * ($у-1) равно $val.\n"; print "Значение \$х остается равным $х, а \$у равным $у.\п";

Результат выполнения:

Значение (9+1) * (11-1) равно 100.

Значение $х остается равным 9, а $у равным 11.

Итак, подпрограмма получает и возвращает параметры через специальный массив @_. Если параметр является массивом или хеш-массивом, его элементы также сохраняются в массиве параметров @_. При передаче в подпрограмму нескольких параметров-массивов или хеш-массивов они утрачивают свою целостность. Иными словами, после записи параметров-массивов (хеш-массивов) в массив @_ из него невозможно выделить отдельный параметр-массив (хеш-массив): все параметры в массиве @_ хранятся единой "кучей". Для сохранения при передаче в подпрограмму целостности массива или хеш-массива существуют два основных подхода.

11.4.1.1. Использование типа typeglob

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

Значается префиксом "*". Префикс "*" можно рассматривать как метасимвол, вместо которого может стоять любой из префиксов "$", "@", "%", "&", обозначающих тип данных "скаляр", "массив", "хеш-массив", "функция" соответственно. Интерпретатор преобразует переменную типа typeglob, например, *abc, в скалярную величину. Эта величина является ссылкой на гнездо в таблице символов, содержащее элементы, разных типов с одинаковым именем abc, и представляет любой из этих элементов. Например, запись *abc обозначает всю совокупность, а также любую из следующих переменных: скаляр $abc, массив @abc, хеш %abc, функция sabc.

(Таблицы символов обсуждаются в главе 12.)

Передача в подпрограмму вместо параметра-массива или хеш-массива соответствующей переменной типа typeglob является имитацией передачи параметра-массива (хеш-массива) по ссылке с сохранением его целостности. Рассмотрим следующий пример.

Sub doublargs {

foreach $item (@mylist) { $item *= 2;

}

foreach $key (keys %myhash) { $myhash{$key} *= 2;

} }

@somelist= (1,2,3); /^~~- """"~\ %somehash=("one"=>5, "two"=>15, "three"=>20); print "начальные значения:\n\@somelist=@somelist\n"; foreach $key (keys %somehash) {

print "\$somehash{$key}=$somehash{$key} ";

}

print "\n";

doublargs(*somelist,*somehash);

print "итоговые значения:\n\@somelist=@somelist\n";

foreach $key (keys %somehash) {

print "\$somehash{$key}=$somehash{$key} "; } print "\n";

Подпрограмма doubiargs принимает на вход массив и хеш-массив и изменяет их элементы, умножая на 2. Вместо массива и хеш-массива в подпрограмму передаются соответствующие переменные типа typeglob, которые легко выделить из массива @_, так как фактически они являются скалярами. Обратите внимание на применение функции local. Использовать вместо нее функцию ту здесь нельзя. Переменная типа typeglob не может быть локальной, она представляет несколько одноименных переменных разных типов из таблицы символов. Далее возникает вопрос, каким образом изменение в подпрограмме массива @myiist влияет на изменение фактического параметра gsomeiist. Дело в том, что операция присваивания вида *х = *у создает синоним *х для гнезда таблицы символов *у, так что осуществление операции над $х, @х, %х эквивалентно осуществлению этой операции над $у, @у, %у. В результате присваивания

Local(*mylist, *myhash) = @_;

Создается псевдоним *myiist для *someiist, поэтому все изменения элементов массива @myiist внутри подпрограммы эквивалентны изменениям элементов массива @someiist. Все сказанное справедливо и для хеш-массивов %myhash и %somehash. Результат подтверждает корректность передачи массива и хеш-массива по ссылке:

начальные значения:

@somelist=l 2 3

$somehash{one}=5 $somehash{three}=20 $somehashftwo)=15

итоговые значения:

@somelist=2 4 6

$somehash{one}=10 $somehash(three}=40 $somehash{two}=30

11.4.1.2. Использование ссылок

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

sub doublparms {

ray ($listref, $hashref) = @_;

foreach $item (@$listref} { $item *= 2;

} .

foreach $key (keys %$hashref) { $$hashref{$key} *= 2;

} }

@somelist=(1,2,3) ;

%somehash=("one"=>5, "two"=>15, "three"=>20); print "начальные значения:\@somelist=@somelist\n"; foreach $key (keys %somehash) { .

print "\$somehash{$key}=$somehash{$key} "; }

print "\n";

doublparms(\@somelist,\%somehash); print "итоговые значения:\n\@somelist=@somelist\n"; foreach $key (keys %somehash) {

print "\$somehash{$key}=$somehash($key} "; } print "\n";

Здесь для описания локальных переменных использована функция ту. Как мы выяснили ранее в этой главе, применение функции ту в подобном случае реализует передачу параметров по значению. Другими словами, их изменение внутри подпрограммы не влияет на фактические параметры. Каким же образом в данном случае осуществляется передача массива и хеш-массива по ссылке? Дело в том, что по значению передаются только ссылки, указывающие на фактические параметры: массив @someiist и хеш-массив %somehash. Используя операции разыменования внутри подпрограммы, мы получаем доступ непосредственно к массиву @someiist и хеш-массиву %somehash, и изменяем их элементы. В результате выполнения данного сценария будет выведено:

Начальные значения:

@somelist=l 2 3

$somehash{one}=5 $somehash{three}=20 $somehash{two}=15

Итоговые значения:

@somelist=2 4 6

$somehash{one}=10 $somehash{three)=40 $somehash{two}=30

11.5. В каких случаях функцию local нельзя заменить функцией ту

В следующих случаях функция local () является незаменимой. П Присваивание временного значения глобальной переменной. В первую очередь это относится к некоторым предопределенным глобальным переменным, таким как $ARGV, $_ и т. д. Рассмотрим пример.

#!/usr/bin/perl $/ = under"; @ARGV = ("а"); $_ = <>;

print "Первое значение области ввода \$_= ", split,"\п"; {

local 0ARGV = ("аа"); local $_ = <>;

print "Второе значение области ввода \$_= ", split,"\п"; }

{

local 8ARGV = ("ааа"); local $_ = <>;

@fields = split;

print "Третье значение области ввода \$_= ", split, "\n";

}

print "Восстановленное значение области ввода \$_= ", split,"\п";

Пусть имеются три файла

"а": "аа": "ааа":

1111 1111 1111 2222 2222 2222 3333 3333 3333

Аааа bbbb cccc dddd eeee ffff gggg hhhh iiii

В приведенной программе используются специальные глобальные переменные $/, $_ И OARGV.

Специальная переменная $/ содержит значение разделителя входных записей, которым по умолчанию является символ новой строки. Присваивание этой переменной неопределенного значения позволяет при помощи одной операции ввода <> считать весь файл, а не только первую строку.

Специальная переменная $_ используется по умолчанию для сохранения вводимых данных, если в операции ввода соответствующий параметр не указан. Она также используется по умолчанию в качестве аргумента функции split о, если в ней не задана строка, подлежащая разбиению на отдельные строки.

Массив @ARGV содержит аргументы командной строки самой программы. Если при вызове программы ей будет передано имя файла, то оно будет сохранено в массиве @ARGV. Операция ввода о применяется к файлам, переданным в программу в качестве аргументов командной строки, т. е. к файлам, имена которых хранятся в массиве @ARGV. В нашем примере программа вызывается без аргументов, поэтому имя входного файла "а" задается внутри программы прямой записью в массив @ARGV. Первая операция ввода о, следовательно, осуществляется из файла "а". Далее следуют два блока операторов, заключенных в фигурные скобки. В каждом из них при помощи функции local () создаются временные значения для глобальных переменных @ARGV и @_. В первом блоке данные считываются из файла "аа" и сохраняются в качестве временного значения глобальной переменной $_, во втором - из файла "ааа" и также сохраняются в качестве следующего временного значения переменной $_. По выходе из второго блока глобальная переменная $_ восстанавливает свое первоначальное значение. В результате выполнения данной программы будет напечатано:

Первое значение области ввода $_= 11111111llllaaaabbbbcccc Второе значение области ввода $_= 222222222222ddddeeeeffff Третье значение области ввода $_= 3333333333 ; 33gggghhhhiiii Восстановленное значение области ввода $_= lilllllllllllaaaabbbbcccc

  • Создание локального дескриптора файла, каталога или локального псевдонима для функции.

В следующем примере функция local о используется для создания локального дескриптора файла внутри блока операторов.

#!/usr/bin/perl

Open(FILEHANDLE,">b");

print FILEHANDLE "Новая строка в файл "b"\n";

{

local *FILEHANDLE;

Open(FILEHANDLE,">bb");

print FILEHANDLE "Новая строка в файл "bb"\n";

close FILEHANDLE; }

{

local *FILEHANDLE;

Open(FILEHANDLE,">bbb");

print FILEHANDLE "Новая строка в файл "bbb"\n" ;

Close FILEHANDLE; > J

print FILEHANDLE "Еще одна строка в файл "b"\n"; close FILEHANDLE;

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

"Ь":

Новая строка в файл "Ь"

Еще одна строка в файл "Ь"

"bb":

Новая строка в файл "bb"

"bbb":

Новая строка в файл "bbb"

Заметьте, что во время выполнения операций с файлами "bb" и "bbb" файл "Ь" остается открытым.

Аналогичным образом может быть определено локальное имя для функции.

#!/usr/bin/perl

# функция NumberOfArgs() возвращает число своих параметров sub NumberOfArgs {

Return $#_ + 1;

) " . . " print "NumberOfArgs: число параметров=", NumberOfArgs(1,2,3,4),"\n"; {

local *Numbers = *NumberOf Args;

print "Numbers: число параметров=", Numbers (1, 2, 3} , "\n"; } {

Local *N = \SNumberOfArgs;

print "N: число параметров=", N(1,2), "\n"; }

Результат выполнения:

NumberOfArgs: число параметров=4 Numbers: число параметров=3 N: число параметров=2

  • Временное изменение элемента массива или хеш-массива.

В следующем примере внутри блока операторов временно изменяется значение одного элемента глобального хеш-массива %ENV, содержащего значение переменной $РАТН, входящей в состав среды интерпретатора UNIX shell.

Tt!/usr/bin/perl

Print "значение переменной среды \$РАТН:\n$ENV{PATH}\n"; {

Local $ENV{PATH} = "/home/mike/bin"; I -print "временное значение переменной среды \$РАТН: $ENV{PATH}\n";

}

print "прежнее значение переменной среды \$РАТН:\n$ENV{PATH}\п";

Результат будет выведен в следующем виде: значение переменной среды $РАТН:

/sbin: /usr/sbin: /usr/bin: /bin: /usr/XHR6/bin: /usr/local/bin: /opt/bin

временное значение переменной среды $РАТН: /home/mike/bin

прежнее значение переменной среды $РАТН:

/sbin: /usr/sbin: /usr/bin: /bin: /usr/XHR6/bin: /usr/local/bin: /opt/bin

11.6. Прототипы

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

Sub func ($$) {

1

Определяет функцию func о с двумя скалярными аргументами. Символы

Для обозначения типа аргумента приведены в табл. 11.1.

Таблица 11.1. Символы, используемые в прототипах для задания типа аргумента

Символ

Тип данных

Скаляр

Массив

Ассоциативный массив

Анонимная подпрограмма

Тип typeglob

Запись вида \char, где char - один из символов табл. 11.1, обозначает что при вызове подпрограммы имя фактического параметра должно обязательно начинаться с символа char. В этом случае в подпрограмму через массив параметров @_ передается ссылка на фактический параметр, указанный при ее вызове. Обязательные параметры в прототипе отделяются от необязательных точкой с запятой.

В табл. 11.2 в качестве примера приведены объявления пользовательских функции nybud,itin(), синтаксис которых соответствует синтаксису встроенных функций buil tin ().

Таблица 11.2. Примеры прототипов

Объявление

Обращение к функции

sub mylink ($$)

mylink $old, $new

sub myvec ($$$)

Myvec $var, $offset, 1

sub myindex ($$;$)

myindex Sgetstring, "substr"

Sub mysyswrite ($$$;$)

mysyswrite $buf, 0, length ($buf) - $off,

vOf f

sub myreverse (@)
mypop garray mytime

Следует иметь в виду, что проверка синтаксиса, задаваемого при помощи прототипа, не осуществляется, если подпрограмма вызвана с использованием префикса &: ssubname.

11.7. Рекурсивные подпрограммы

Язык Perl допускает, чтобы подпрограмма вызывала саму себя. Такая подпрограмма называется рекурсивной. При написании рекурсивных подпрограмм следует иметь в виду, что все переменные, значения которых изменяются внутри подпрограммы, должны быть локальными, т. е. объявленными при помощи функций ту (} или local (). В этом случае при каждом вызове подпрограммы создается Новая копия переменной. Это позволяет избежать неопределенности и замещения текущего значения переменной ее значением из следующего вызова подпрограммы.

Рекурсивные подпрограммы следует применять осторожно. Многие алгоритмы, являющиеся по сути итеративными, можно реализовать при помощи рекурсивной подпрограммы. Однако такая подпрограмма окажется неэффективной по времени выполнения и потребляемым ресурсам памяти. Вместе с тем, существуют задачи, решить которые можно только при помощи рекурсивных алгоритмов. В этом случае применение рекурсивных подпрограмм является не только вполне оправданным, но и необходимым. Одной из таких задач, которую операционная система решает постоянно, является рекурсивный просмотр дерева каталогов. Рассмотрим пример рекурсивной Peri-подпрограммы tree (), которая делает то же самое: просматривает дерево каталогов, начиная с каталога, заданного параметром подпрограммы, и выводит список файлов, содержащихся в каждом подкаталоге.

sub tree {

local (*ROOT);

my ($root)=$_;

Однако, если явно указать (с помощью пустых скобок), что функция вызывается без параметров, то всё в порядке:
sub my_sub { &inner(); }
И вывод будет выглядеть вот так:
$VAR1 = ;

Впрочем, вызовы функций с использованием амперсанда используются очень редко и в коде почти не встречаются.

Анонимные функции

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

Элементарное объявление анонимной функции в Perl:
my $subroutine = sub { my $msg = shift; printf "I am called with message: %s\n", $msg; return 42; }; # $subroutine теперь ссылается на анонимную функцию $subroutine->("Oh, my message!");
Анонимные функции можно и нужно использовать как для создания блоков кода, так и для замыканий, о которых речь дальше.

Замыкания

Замыкание - это особый вид функции, в теле которой используются переменные, объявленные вне тела этой функции.

В записи это выглядит как, например, функция, находящаяся целиком в теле другой функции.
# возвращает ссылку на анонимную функцию sub adder($) { my $x = shift; # в котором x - свободная переменная, return sub ($) { my $y = shift; # а y - связанная переменная return $x + $y; }; } $add1 = adder(1); # делаем процедуру для прибавления 1 print $add1->(10); # печатает 11 $sub1 = adder(-1); # делаем процедуру для вычитания 1 print $sub1->(10); # печатает 9
Замыкания использовать полезно, например, когда необходимо получить функцию с уже готовыми параметрами, которые будут в ней сохранены. Или же для генерации функции-парсера, колбеков.

Бесскобочные функции

На наш взгляд, это самый подходящий перевод термина parenthesis-less.

Например, print часто пишется и вызывается без скобок. Возникает вопрос, а можем ли мы тоже создавать такие функции?

Безусловно. Для этого у Perl есть даже специальная прагма - subs. Предположим, нам нужна функция, проверяющая значение переменной на истинность.
use strict; use subs qw/checkflag/; my $flag = 1; print "OK" if checkflag; sub checkflag { return $flag; }
Эта программа напечатает OK.

Но это не единственный способ. Perl хорошо продуман, поэтому, если мы реструктуризируем нашу программу и приведём её к такому виду:
use strict; my $flag = 1; sub checkflag { return $flag; } print "OK" if checkflag; …то результат будет тот же.

Закономерность здесь следующая - мы можем вызывать функцию без скобок в нескольких случаях:

  • используя прагму subs;
  • написав функцию ПЕРЕД её вызовом;
  • используя прототипы функций.
Обратимся к последнему варианту.

Прототипы функций


Зачастую разное понимание цели этого механизма приводит к холиварам с адептами других языков, утверждающих, что «у перла плохие прототипы». Так вот, прототипы в Perl не для жёсткого ограничения типов параметров, передаваемых функциям. Это подсказка для языка: как разбирать то, что передаётся в функцию.

Есть, к примеру, абстрактная функция, которая называется my_sub:
sub my_sub { print join ", ", @_; }
Мы её вызываем следующим образом:
my_sub(1, 2, 3, 4, 5);
Функция напечатает следующее:
1, 2, 3, 4, 5,

Получается, что в любую функцию Perl можно передать любое количество аргументов. И пусть сама функция разбирается, что мы от неё хотели.

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

Функция Perl с прототипами будет выглядеть так:
sub my_sub($$;$) { my ($v1, $v2, $v3) = @_; $v3 ||= "empty"; printf("v1: %s, v2: %s, v3: %s\n", $v1, $v2, $v3); }
Прототипы функций записываются после имени функции в круглых скобках. Прототип $$;$ означает, что в качестве параметров необходимо присутствие двух скаляров и третьего по желанию, «;» отделяет обязательные параметры от возможных.

Если же мы попробуем вызвать её вот так:
my_sub(); …то получим ошибку вида:
Not enough arguments for main::my_sub at pragmaticperl.pl line 7, near "()"
Execution of pragmaticperl.pl aborted due to compilation errors.

А если так:
&my_sub(); …то проверка прототипов не будет происходить.

Резюмируем. Прототипы будут работать в следующих случаях:

  • Если функция вызывается без знака амперсанда (&). Perlcritic (средство статического анализа Perl кода), кстати говоря, ругается на запись вызова функции через амперсанд, то есть такой вариант вызова не рекомендуется.
  • Если функция написана перед вызовом. Если мы сначала вызовем функцию, а потом её напишем, при включённых warnings получим следующее предупреждение:
    main::my_sub() called too early to check prototype at pragmaticperl.pl line 4
Ниже пример правильной программы с прототипами Perl:
use strict; use warnings; use subs qw/my_sub/; sub my_sub($$;$) { my ($v1, $v2, $v3) = @_; $v3 ||= "empty"; printf("v1: %s, v2: %s, v3: %s\n", $v1, $v2, $v3); } my_sub();
В Perl существует возможность узнать, какой у функции прототип. Например:
perl -e "print prototype("CORE::read")"
Выдаст:
*\$$;$

Оверрайд методов

Оверрайд - часто довольно полезная штука. Например, у нас есть модуль, который писал некий N. И всё в нём хорошо, а вот один метод, допустим, call_me, должен всегда возвращать 1, иначе беда, а метод из базовой поставки модуля возвращает всегда 0. Код модуля трогать нельзя.

Пусть программа выглядит следующим образом:
use strict; use Data::Dumper; my $obj = Top->new(); if ($obj->call_me()) { print "Purrrrfect\n"; } else { print "OKAY:(\n"; } package Top; use strict; sub new { my $class = shift; my $self = {}; bless $self, $class; return $self; } sub call_me { print "call_me from TOP called!\n"; return 0; } 1;
Она выведет:
call_me from TOP called!
OKAY:(

И снова у нас есть решение.

Допишем перед вызовом $obj->call_me() следующую вещь:
*Top::call_me = sub { print "Overrided subroutine called!\n"; return 1; };
А ещё лучше, для временного оверрайда используем ключевое слово local:
local *Top::call_me = sub { print "Overrided subroutine called!\n"; return 1; };
Это заменит функцию call_me пакета Top в лексической области видимости (в текущем блоке).
Теперь наш вывод будет выглядеть так:
Overrided subroutine called!
Purrrrfect

Код модуля не меняли, функция теперь делает то, что нам надо.

На заметку: если приходится часто использовать данный приём в работе - налицо архитектурный косяк. Хороший пример использования - добавление вывода отладочной информации в функции.

Wantarray

В Perl есть такая полезная штука, которая позволяет определить, в каком контексте
вызывается функция. Например, мы хотим, чтобы функция вела себя следующим образом:
когда надо возвращала массив, а иначе - ссылку на массив. Это можно реализовать, и
к тому же очень просто, с помощью wantarray. Напишем простую программу для демонстрации:
#!/usr/bin/env perl use strict; use Data::Dumper; my @result = my_cool_sub(); print Dumper @result; my $result = my_cool_sub(); print Dumper $result; sub my_cool_sub { my @array = (1, 2, 3); if (wantarray) { print "ARRAY!\n"; return @array; } else { print "REFERENCE!\n"; return \@array; } }
Что выведет:
ARRAY!
$VAR1 = 1;
$VAR2 = 2;
$VAR3 = 3;
REFERENCE!
$VAR1 = [
1,
2,
3
];

Также хотелось бы напомнить про интересную особенность Perl. %hash = @аrray; В этом случае Perl построит хэш вида ($array => $array, $array => $array);

Посему, если применять my %hash = my_cool_sub(), будет использована ветка логики wantarray. И именно по этой причине wanthash нет.

AUTOLOAD

В Perl одна из лучших систем управления модулями. Мало того что программист может контролировать все стадии исполнения модуля, так ещё существуют интересные особенности, которые делают жизнь проще. Например, AUTOLOAD.

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

Например:
#!/usr/bin/env perl use strict; Autoload::Demo::hello(); Autoload::Demo::asdfgh(1, 2, 3); Autoload::Demo::qwerty(); package Autoload::Demo; use strict; use warnings; our $AUTOLOAD; sub AUTOLOAD { print $AUTOLOAD, " called with params: ", join (", ", @_), "\n"; } sub hello { print "Hello!\n"; } 1;
Очевидно, что функций qwerty и asdfgh не существует в пакете Autoload::Demo. В функции AUTOLOAD специальная глобальная переменная $AUTOLOAD устанавливается равной функции, которая не была найдена.

Вывод этой программы:
Hello!
Autoload::Demo::asdfgh called with params: 1, 2, 3
Autoload::Demo::qwerty called with params:

Генерация функций на лету

Допустим, нам нужно написать множество функций, выполняющих примерно одинаковые действия. Например, набор аксессоров у объекта. Написание подобного кода вряд ли кому-то доставит удовольствие:
sub getName { my $self = shift; return $self->{name}; } sub getAge { my $self = shift; return $self->{age}; } sub getOther { my $self = shift; return $self->{other}; }
Это Perl. «Лень, нетерпение, надменность» (Л. Уолл).

Функции можно генерировать. В Perl есть такая штука как тип данных typeglob. Наиболее точный перевод названия - таблица имён. Typeglob имеет свой сигил - «*».

Для начала посмотрим код:
#!/usr/bin/env perl use strict; use warnings; package MyCoolPackage; sub getName { my $self = shift; return $self->{name}; } sub getAge { my $self = shift; return $self->{age}; } sub getOther { my $self = shift; return $self->{other}; } foreach (keys %{*MyCoolPackage::}) { print $_." => ".$MyCoolPackage::{$_}."\n"; }
Вывод:
getOther => *MyCoolPackage::getOther
getName => *MyCoolPackage::getName
getAge => *MyCoolPackage::getAge

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

И вот что у нас получилось:
#!/usr/bin/env perl use strict; use warnings; $\ = "\n"; my $person = Person->new(name => "justnoxx", age => "25", other => "perl programmer",); print "Name: ", $person->get_name(); print "Age: ", $person->get_age(); print "Other: ", $person->get_other(); package Person; use strict; use warnings; sub new { my ($class, %params) = @_; my $self = {}; no strict "refs"; for my $key (keys %params) { # __PACKAGE__ равен текущему модулю, это встроенная # волшебная строка # следующая строка превращается в, например: # Person::get_name = sub {...}; *{__PACKAGE__ . "::" . "get_$key"} = sub { my $self = shift; return $self->{$key}; }; $self->{$key} = $params{$key}; } bless $self, $class; return $self; } 1;
Эта программа напечатает:
Name: justnoxx
Age: 25
Other: perl programmer

Атрибуты функций

В Python есть такое понятие как декоратор. Это такая штуковина, которая позволяет «добавить объекту дополнительное поведение».

Да, в Perl декораторов нет, зато есть атрибуты функций. Если мы откроем perldoc perlsub и посмотрим на описание функции, то увидим любопытную запись:
sub NAME(PROTO) : ATTRS BLOCK
Таким образом, функция с атрибутами может выглядеть так:
sub my_sub($$;$) : MyAttr { print "Hello, I am sub with attributes and prototypes!"; }
Работа с атрибутами в Perl - дело нетривиальное, потому уже довольно давно в стандартную поставку Perl входит модуль Attribute::Handlers.

Дело в том, что атрибуты из коробки имеют довольно много ограничений и нюансов работы, так что, если кому-то интересно, можно обсудить в комментариях.

Допустим, у нас есть функция, которая может быть вызвана только в том случае, если пользователь авторизован. За то, что пользователь авторизован отвечает переменная $auth, которая равна 1, если пользователь авторизован, и 0, если нет. Мы можем сделать следующим образом:
my $auth = 1; sub my_sub { if ($auth) { print "Okay!\n"; return 1; } print "YOU SHALL NOT PASS!!!1111"; return 0; }
И это приемлемое решение.

Но может возникнуть такая ситуация, что функций будет становиться больше и больше. А в каждой делать проверку будет всё накладнее. Проблему можно решить с помощью атрибутов.
use strict; use warnings; use Attribute::Handlers; use Data::Dumper; my_sub(); sub new { return bless {}, shift; } sub isAuth: ATTR(CODE) { my ($package, $symbol, $referent, $attr, $data, $phase, $filename, $linenum) = @_; no warnings "redefine"; unless (is_auth()) { *{$symbol} = sub { require Carp; Carp::croak "YOU SHALL NOT PASS\n"; }; } } sub my_sub: isAuth { print "I am called only for auth users!\n"; } sub is_auth { return 0; }
В данном примере вывод программы будет выглядеть так:
YOU SHALL NOT PASS at myattr.pl line 18. main::__ANON__() called at myattr.pl line 6

А если мы заменим return 0 на return 1 в is_auth, то:
I am called only for auth users!

Не зря атрибуты представлены в конце статьи. Для того чтобы написать этот пример, мы воспользовались:

  • анонимными функциями
  • оверрайдом функций
  • специальной формой оператора goto
Несмотря на довольно громоздкий синтаксис, атрибуты успешно и активно применяются, например, в веб-фреймворке Catalyst. Однако, не стоит забывать, что атрибуты, всё-таки, являются экспериментальной фичей Perl, а потому их синтаксис может меняться в следующих версиях языка.

Статья написана в соавторстве и по техническому материалу от Дмитрия Шаматрина (@justnoxx) и при содействии программистов REG.RU: Тимура Нозадзе (@TimurN), Виктора Ефимова (@vsespb), Полины Шубиной (@imagostorm), Andrew Nugged (@nugged)

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

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

Например, если ваша программа содержит функцию, которая вычисляет площадь круга, следующей строкой Вы можете обратиться к ней:

$areaOfFirstCircle = areaOfCircle($firstRadius);

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

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

Давайте присмотримся к вызову функции - сначала мы видим скалярную переменную, затем - оператор присвоения. Вы уже знаете, что это означает - Perl присвоит переменной $areaOfFirstCircle значение, стоящее справа от знака присваивания. Но что в действительности стоит справа?

Первое, что вы видите - это имя функции areaOfCircle(). Круглые скобки справа и отсутствие перед именем символов $, @ и % говорит о том, что это - вызов функции. Внутри круглых скобок содержится список параметров или значений, передающихся функции.

Вычисление площади круга:

$areaOfFirstCircle = areaOfCircle(5);
print("$areaOfFirstCircle\n");
sub areaOfCircle {
$radius = $_;
return(3.1415 * ($radius ** 2));
}

Программа напечатает:

Объявление функции:

sub имяФункции {
тело функции
}

И все. Ваша функция готова.

Сложнее дело обстоит с параметрами. Параметры - это значения, которые мы передаем в функцию. Параметры содержатся внутри круглых скобок, следующих сразу за именем функции. В примере выше вызов функции - это areaOfCircle(5). Здесь мы использовали только один параметр. Но даже в том случае, если функция имеет только один параметр, Perl при вызове функции создает массив параметров для использования их функцией.

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

Наша маленькая функция из примера выше могла бы сделать это следующей строкой:

$radius = $_;

Эта строка присваивает первый элемент массива @_ скалярной переменной $radius.

Если вы хотите, вы можете не использовать в функции оператор return для возврата значения, - Perl автоматически возвратит значение последнего вычисленного выражения. Но будет лучше, если вы все же будете использовать оператор return - так вы избежите многих случайных ошибок.

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

Использование массива параметров (@_)

Как уже говорилось ранее - все параметры функция может найти в массиве параметров @_. Это очень удобно - чтобы узнать, сколько параметров было передано функции, нужно всего лишь обратиться к массиву параметров @_ в скалярном контексте.

firstSub(1, 2, 3, 4, 5, 6);
firstSub(1..3);
firstSub("A".."Z");
sub firstSub {
$numParameters = @_ ;
print("The number of parameters is $numParameters\n");
}

Программа напечатает:

The number of parameters is 6

The number of parameters is 3

The number of parameters is 26

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

Конечно, это не очень удобно - обращаться к переданным функции параметрам по их номерам - @_ или @_. Вы можете воспользоваться более удобной технологией:

areaOfRectangle(2, 3);
areaOfRectangle(5, 6);
sub areaOfRectangle {
($height, $width) = @_ ;
$area = $height * $width;
print("The height is $height. The width is $width. The area is $area.\n\n");
}

Программа напечатает:

The height is 2. The width is 3. The area is 6.

The height is 5. The width is 6. The area is 30.

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

@array = (0..5);

firstSub(@array);

sub firstSub{
$_ = "A";
$_ = "B";
}

Программа напечатает:

After fuNCtion call, array = A B 2 3 4 5

Как вы видите, функция изменила значение переданных ей параметров, и это повлияло также на работу остальной программы - значения массива @array изменились не только для функции, но и для всей остальной программы. Это плохая практика программирования - если у вас нет именно такой конкретной цели, то никогда не пользуйтесь подобными премами - они чреваты неочевидными ошибками. С другой стороны, если вы в начале функции присваиваете значения переданных параметров новым переменным (как было показано ранее), и работаете в дальнейшем только с ними - такой проблемы у вас не возникнет - ведь вы в данном случае на самом деле не изменяете значений переданных функции параметров.

Вот пример той же программы, но написанной более правильно:

@array = (0..5);
print("Before fuNCtion call, array = @array\n");
firstSub(@array);
print("After fuNCtion call, array = @array\n");
sub firstSub{

$firstVar = "A";
$secondVar = "B";
}

Программа напечатает:

Before fuNCtion call, array = 0 1 2 3 4 5

After fuNCtion call, array = 0 1 2 3 4 5

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

Но тогда вы можете столкнуться с другой проблемой:

$firstVar = 10;
@array = (0..5);
print("Before fuNCtion call\n");

print("\tarray = @array\n");
firstSub(@array);
print("After fuNCtion call\n");
print("\tfirstVar = $firstVar\n");
print("\tarray = @array\n");
sub firstSub{
($firstVar, $secondVar) = @_ ;
$firstVar = "A";
$secondVar = "B";
}

Программа напечатает:

Before fuNCtion call
firstVar = 10
array = 0 1 2 3 4 5

After fuNCtion call
firstVar = A
array = 0 1 2 3 4 5

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

Области действия переменных.

Области действия переменной - это те фрагменты кода, в которых вы можете использовать данную переменную. По умолчанию любая переменная в Perl-программы "видна" из любой точки программы.

Иногда бывает очень полезным ограничить область действия той или иной переменной внутри какой-либо функции. В этом случае изменение значения переменной внутри функции никак не отразится на остальных частях программы. В Perl имеются две функции - my() и local(). Первая создает переменную, которая видна только внутри данной функции. Вторая создает переменную, которую также могут "видеть" функции, вызванные из данной функции.

firstSub("AAAAA", "BBBBB");
sub firstSub{
local ($firstVar) = $_;
my($secondVar) = $_;


secondSub();
print("firstSub: firstVar = $firstVar\n");
print("firstSub: secondVar = $secondVar\n\n");
}

Sub secondSub{


$firstVar = "ccccC";
$secondVar = "DDDDD";
print("secondSub: firstVar = $firstVar\n");
print("secondSub: secondVar = $secondVar\n\n");
}

Программа напечатает:

firstSub: firstVar = AAAAA
firstSub: secondVar = BBBBB
secondSub: firstVar = AAAAA
Use of uninitialized value at test.pl line 19.
secondSub: secondVar =
secondSub: firstVar = ccccC
secondSub: secondVar = DDDDD
firstSub: firstVar = ccccC
firstSub: secondVar = BBBBB

Как вы видите, функция secondSub() не имеет доступа к переменной $secondVar, которая была создана функцией my() внутри функции firstSub(). Perl даже вывел сообщение, предупреждая вас об этом. В то же время, переменная $firstVar доступна и может быть изменена функцией secondSub().

По возможности старайтесь обходиться только функцией my() и не использовать функцию local() - так вы обеспечите себе более строгий контроль над областью действия переменных.

На самом деле, функция my() гораздо более сложна и функциональна. Но об этом пойдет речь в главе 15 - "Модули Perl".

Вы помните, в чем разница между передачей параметров функции по ссылке и по значению? Если вы передаете параметры по значению, то функция не может изменять значения переданных ей параметров (переменных), а значит это никак не отразится на всей программе. Если же вы передаете параметры по ссылке, то функция может изменять значения параметров (переменных), и это отразится на основной программе. Так вот, при использовании функции local() метод передачи параметров по ссылке работает немного по-другому - функции могут менять значения переданных им параметров (переменных), но это отразится только на самой "верхней" функции - той, где была использована функция local(), - на основную же программу это никак не повлияет.

Использование списка в качестве параметра функции.

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

firstSub((0..10), "AAAA");
sub firstSub{
local(@array, $firstVar) = @_ ;

print("firstSub: firstVar = $firstVar\n");
}

Программа напечатает:

firstSub: array = 0 1 2 3 4 5 6 7 8 9 10 AAAA
Use of uninitialized value at test.pl line 8.
firstSub: firstVar =

Вы видите, что при инициализации переменных массив @array поглощает все элементы массива параметров @_, ничего не оставляя скаларной переменной $firstVar. Это подтверждает и предупреждающее сообщение интерпретатора Perl. Вы можете решить данную проблему путем перестановки местами параметров - если вы поставите сначала скалярную переменную, а потом массив, то все будет так как надо:

firstSub("AAAA", (0..10));
sub firstSub{
local($firstVar, @array) = @_ ;
print("firstSub: array = @array\n");
print("firstSub: firstVar = $firstVar\n");
}

Программа напечатает:

firstSub: array = 0 1 2 3 4 5 6 7 8 9 10
firstSub: firstVar = AAAA

Вы можете передавать в функцию сколько угодно скалярных величин, но только один массив. Если вы попытаетесь передать в функцию несколько массивов, их элементы просто сольются в один массив, и функция не сможет узнать, - где заканчивается один массив и начинается другой.

Вложенные (рекурсивные) функции.

Функции могут вызывать сами себя, углубляясь на несколько уровней. На сколько - это зависит от конкретной реализации Perl, от конкретной аппаратной части и от конфигурации. Обычно, вам хватит ресурсов вашей машины, и беспокоиться об этом не стоит. Но если вам интересно, вы можете попробовать запустить вот такую программу:

FirstSub();
sub firstSub{
print("$count\n");
$count++;
firstSub();
}

Программа напечатает:

Error: Runtime exception

Перед тем, как напечатать это сообшение об ошибке, программа будет увеличивать значение переменной $count до тех пор, пока будет возможен рекурсивный вызов функции. Таким образом, по значению переменной $count вы можете узнать, сколько уровней рекурсивных вызовов допускает ваша система.

Но не увлекайтесь рекурсией - этим стоит заниматься вплотную в особенных случаях, например, при некоторых математических рассчетах.

Частные функции.

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

$temp = performCalc(10, 10);
print("temp = $temp\n");
sub performCalc {
my ($firstVar, $secondVar) = @_;
my $square = sub {
return($_ ** 2);
};
return(&$square($firstVar) + &$square($secondVar));
};

Программа напечатает:

Как вы видите, у нас имеется функция $square. Точнее, $square - это обычная скалярная переменная, ссылающаяся на функцию. Для обращения к функции мы используем знак & перед именем переменной $square. Эту функцию мы можем использовать только внутри функции performCalc() - она недоступна для остальной программы.



В продолжение темы:
Windows

Часть вторая : "Важнейшие характеристики каждого семейства процессоров Intel Core i3/i5/i7. Какие из этих чипов представляют особый интерес" Введение Сначала мы приведём...

Новые статьи
/
Популярные