HTML page
Подпрограммы
Введение
Практика вставки/копирования кода довольно опасна, поэтому в больших программах многократно используемые фрагменты кода часто оформляются в виде подпрограмм. Для нас термины "подпрограмма" (subroutine) и "функция" (function) будут эквивалентными, поскольку в Perl они различаются ничуть не больше, чем в С. Даже объектно-ориентированные методы представляют собой обычные подпрограммы со специальным синтаксисом вызова, описанным в главе 13 "Классы, объекты и связи". Подпрограмма объявляется с помощью ключевого слова sub. Пример определения простой подпрограммы выглядит так:sub hello {
$greeted++; # Глобальная переменная
print "hi there\n!";
}
Типичный способ вызова этой подпрограммы выглядит следующим образом:
hello(); # Подпрограмма hello вызывается без аргументов/параметров
Перед выполнением программы Perl компилирует ее, поэтому место объявления подпрограммы не имеет значения. Определения не обязаны находиться в одном файле с основной программой. Они могут быть взяты из других файлов с помощью операторов do, require или use (см. главу 12 "Пакеты, библиотеки и модули"), создаваться "на месте" с помощью ключевого слова eval или механизма AUTOLOAD или генерироваться посредством замыканий, используемых в шаблонах функций. Если вы знакомы с другими языками программирования, некоторые особенности функций Perl могут показаться странными. В большинстве рецептов этой главы показано, как применять эти особенности в свою пользу. o Функции Perl не имеют формальных, именованных параметров, но это не всегда плохо (см. рецепты 10.1 и 10.7). o Все переменные являются глобальными, если обратное не следует из объявления. Дополнительная информация приведена в рецептах 10.1 и 10.7. o Передача или возвращение нескольких массивов или хэшей обычно приводит к потере ими "индивидуальности". О том, как избежать этого, рассказано в рецептах 10.5, 10.8, 10.9 и 10.11. o Функция может узнать свой контекст вызова (списковый или скалярный), количество аргументов при вызове и даже имя функции, из которой она была вызвана. О том, как это сделать, рассказано в рецептах 10.4 и 10.6. o Используемое в Perl значение undef может использоваться в качестве признака ошибки, поскольку ни одна допустимая строка или число никогда не принимает это значение. В рецепте 10.10 описаны некоторые неочевидные трудности, связанные с undef, которых следует избегать, а в рецепте 10.12 показано, как обрабатываются другие катастрофические случаи. o В Perl функции обладают рядом интересных возможностей, редко встречи ющихся в других языках (например, анонимные функции, создание функ ций "на месте" и их косвенный вызов через указатель на функцию). Эти мистические темы рассматриваются в рецептах 10.14 и 10.16. При вызове вида $х = &func; функция не получает аргументов, по зато может напрямую обращаться к массиву @_ вызывающей стороны! Если убрать ампер-санд и воспользоваться формой func() или func, создается новый, пустой экземпляр массива @_.
10.1. Доступ к аргументам подпрограммы
Проблема
В своей функции вы хотите использовать аргументы, переданные вызывающей стороной.Решение
Все значения, переданные 4)ункции в качестве аргументов, хранятся в специальном массиве @_. Следовательно, первый аргумент хранится в элементе $_[0], второй - в $_[1] и т. д. Общее число аргументов равно scalar(@_). Например:sub hypotenuse {
return sqrt( ($_[0] ** 2) + ($_[1J ** 2) );
}
$diag = hypotenuse(3,4); # $diag = 5
В начале подпрограммы аргументы почти всегда копируются в именованные закрытые переменные для удобства и повышения надежности:
sub hypotenuse {
my ($side1, $side2) = @_;
return sqrt( ($side1 ** 2) + ($slde1 ** 2) );
}
Комментарий
Говорят, в программировании есть всего три удобных числа: ноль, единица и "сколько угодно". Механизм работы с подпрограммами Perl разрабатывался для упрощения написания функций со сколь угодно большим (или малым) числом параметров и возвращаемых значений. Все входные параметры хранятся в виде отдельных скалярных значений в специальном массиве @_, который автоматически становится локальным для каждой функции (см. рецепт 10.13). Для возвращения значений из подпрограмм следует использовать команду return с аргументом. Если она отсутствует, возвращаемое значение представляет собой результат по следнего вычисленного выражения. Приведем несколько примеров вызова функции hypotenuse, определенной в решении:print hypotenuse(3, 4), "\n"; # Выводит 5
@а = (3, 4);
print hypotenuse(@a), "\n"; # Выводит 5
Если взглянуть на аргументы, использованные во втором вызове hypotenuse, может показаться, что мы передали лишь один аргумент - массив @а. Но это не так - элементы @а копируются в массив @_ по отдельности. Аналогично, при вызове функции с аргументами (@а, @Ь) мы передаем ей все аргументы из обоих массивов. При этом используется тот же принцип, что и при сглаживании списков:
@both = (@men, @women);
Скалярные величины в ^представляют собой неявные синонимы для передаваемых значений, а не их копии. Таким образом, модификация элементов @_ в подпрограмме приведет к изменению значений на вызывающей стороне. Это тяжкое наследие пришло из тех времен, когда в Perl еще не было нормальных ссылок. Итак, функцию можно записать так, чтобы она не изменяла свои аргументы -для этого следует скопировать их в закрытые переменные:
@nums = (1,4, 3,5, 6.7);
@ints = int_all(@nums); # @nums не изменяется
sub int_all {
my @retlist = @_; # Сделать копию для return
for my $n (@retlist) { $n = int($n) }
return @retlist;
}
В начале подпрограммы аргументы почти всегда копируются в именованные закрытые переменные для удобства и повышения надежности:
sub hypotenuse {
my ($side1, $side2) = @_;
return sqrt( ($side1 ** 2) + ($side1 ** 2) );
}
Комментарий
Говорят, в программировании есть всего три удобных числа: ноль, единица и "сколько угодно". Механизм работы с подпрограммами Perl разрабатывался для упрощения написания функций со сколь угодно большим (или малым) числом параметров и возвращаемых значений. Все входные параметры хранятся в виде отдельных скалярных значений в специальном массиве @_, который автоматически становится локальным для каждой функции (см. рецепт 10.13). Для возвращения значений из подпрограмм следует использовать команду return с аргументом. Если она отсутствует, возвращаемое значение представляет собой результат последнего вычисленного выражения. Приведем несколько примеров вызова функции hypotenuse, определенной в решении:print hypotenuse(3, 4), "\n"; # Выводит 5
@а = (3, 4);
print hypotenuse(@a), "\n"; # Выводит 5 Если взглянуть на аргументы, использованные во втором вызове hypotenuse, может показаться, что мы передали лишь один аргумент - массив @а. Но это не так - элементы @а копируются в массив @_ по отдельности. Аналогично, при вызове функции с аргументами (@а, @Ь) мы передаем ей все аргументы из обоих массивов. При этом используется тот же принцип, что и при сглаживании списков:
@both = (@men, @women);
Скалярные величины в @_ представляют собой неявные синонимы для передаваемых значений, а не их копии. Таким образом, модификация элементов @>_ в подпрограмме приведет к изменению значений на вызывающей стороне. Это тяжкое наследие пришло из тех времен, когда в Perl еще не было нормальных ссылок. Итак, функцию можно записать так, чтобы она не изменяла свои аргументы -для этого следует скопировать их в закрытые переменные:
@nums = (1.4, 3.5, 6.7);
@ints = int_all(@nums); # @nums не изменяется
sub int_all {
vmy @retlist = @_; # Сделать копию для return
for my $n (@retlist) { $n = int($n) }
return @retlist;
Впрочем, функция также может изменять значения, переменных вызывающей стороны:
@nums = (1.4, 3.5. 6.7);
trunc_em(@nums);
@nums = (1,3,6)
sub trunc_em {
for (@_) { $_ = int($_) } # Округлить каждый аргумент
}
Таким функциям не следует передавать константы - например, trunc_em(l .4, 3.5, 6.7). Если попытаться это сделать, будет возбуждено исключение Modification of a read-only value attempted at... ("Попытка модифицировать величину, доступную только для чтения"). Встроенные функции спориспотр работают именно так - они модифицируют переменные вызывающей стороны и возвращают удаленный символ(-ы). Многие привыкают к тому, что функции возвращают измененные значения, и часто пишут в программах следующее:
$line = chomp(<>); # НЕВЕРНО
пока не поймут, что происходит в действительности. Учитывая широкие возможности для ошибок, перед модификацией @_ в подпрограмме стоит дважды подумать.
> Смотри также ------------------------------
perlsub(1).
10.2. Создание закрытых переменных в функциях
Проблема
В подпрограмме потребовалось создать временную переменную. Использование глобальных переменных нежелательно, поскольку другие подпрограммы-могут получить к ним доступ.Решение
Воспользуйтесь ключевым словом ту для объявления переменной, ограниченной некоторой областью программы:sub somefunc {
my $variable; # Переменная $variable невидима
# за пределами somefunc()
my ($another, @an_array, %a_hash); # Объявляем несколько
# переменных сразу
#...
}
Комментарий
Оператор my ограничивает использование переменной и обращение к ней определенным участком программы. За пределами этого участка переменная недоступна. Такой участок называется областью действия (scope). Переменные, объявленные с ключевым словом ту, обладают лексической областью действия - это означает, что они существуют лишь в границах некоторого фрагмента исходного текста. Например, областью действия переменной $variable из решения является функция somefunc, в которой она была определена. Переменная создается при вызове somefunc и уничтожается при ее завершении. Переменная доступна внутри функции, но не за ее пределами. Лексическая область действия обычно представляет собой программный блок, заключенный в фигурные скобки, - например, определение тела подпрограммы somefunc или границы команд if, while, for, foreach и eval. Лексическая область действия также может представлять собой весь файл или строку, переданную eval. Поскольку лексическая область действия обычно является блоком, иногда мы говорим, что лексические переменные (переменные с лексической областью действия) видны только в своем блоке - имеется в виду, что они видны только в границах своей области действия. Простите нам эту неточность, иначе слова "область действия" и "подпрограмма" заняли бы половину этой книги. Поскольку фрагменты программы, в которых видна переменная ту, определяются во время компиляции и не изменяются позднее, лексическая область действия иногда не совсем точно называется "статической областью действия". Ее противоположностью является динамическая область действия, рассмотренная в рецепте 10.13. Объявление ту может сочетаться с присваиванием. При определении сразу нескольких переменных используются круглые скобки:mу ($nаmе, $аgе) = @argv;
mу $start = fetch_time();
Эти лексические переменные ведут себя как обычные локальные переменные., Вложенный блок видит лексические переменные, объявленные в родительских по отношению к нему блоках, но не в других, не связанных с ними блоках:
my ($a, $b) = @pair;
mу $с = fetch_time();
sub check_x {
mу $х = $_[0];
mу $у = "whatever";
run_check();
if ($condition) { print "got $x\n";
} }
В приведенном выше фрагменте блок if внутри функции может обращаться i закрытой переменной $х. Однако в функции run_check, вызванной из этой облас' ти, переменные $х и $у недоступны, потому что она предположительно определяется в другой области действия. Однако check_x может обращаться к $а, $Ь и $с из внешней области, поскольку определяется в одной области действия с этими переменными. Именованные подпрограммы не следует объявлять внутри объявлений других именованных подпрограмм. Такие подпрограммы, в отличие от полноценных замыканий, не обеспечивают правильной привязки лексических переменных. В рецепте 10.16 показано, как справиться с этим ограничением.При выходе лексической переменной за пределы области действия занимаемая ей память освобождается, если на нее не существует ссылок, как для массива @arguments в следующем фрагменте:
sub save_array {
my (@arguments = @_;
push (@Global_Array, \@arguments);
}
Система сборки мусора Perl знает о том, что память следует освобождать лишь для неиспользуемых объектов. Это и позволяет избежать утечки памяти при возвращении ссылки на закрытую переменную.
> Смотри также -------------------------------
Раздел "Private Variables via my()" perlsub(1).
10.3. Создание устойчивых закрытых переменных
Проблема
Вы хотите, чтобы переменная сохраняла значение между вызовами подпрограммы, но не была доступна за ее пределами. Например, функция может запоминать, сколько раз она была вызвана.Решение
"Заверните" функцию во внешний блок и объявите переменные ту в области действия этого блока, а не в самой функции:{
my $variable;
sub mysub {
# ... обращение к $variable
} }
Если переменные требуют инициализации, снабдите блок ключевым словом BEGIN, чтобы значение переменных заведомо задавалось перед началом работы основной программы:
BEGIN {
my $variable =1; # Начальное значение
sub othersub { #... обращение к $variable
} }
Комментарий
В отличие от локальных переменных в языках С и C++, лексические переменные Perl не всегда уничтожаются при выходе из области действия. Если нечто, продолжающее существовать, все еще помнит о лексической переменной, память не освобождается. В нашем примере mysub использует переменную $variable, поэтому Perl не освобождает память переменной при завершении блока, вмещающего определение mysub. Счетчик вызовов реализуется следующим образом:{
my $counter;
sub next_counter { return ++$counter }
} .
При каждом вызове next_counter функция увеличивает и возвращает переменную $counter. При первом вызове переменная $counter имеет неопределенное значение, поэтому для оператора ++ она интерпретируется как 0. Переменная входит не в область действия next_counter, а в окружающий ее блок. Никакой внешний код не сможет изменить $counter без вызова next_counter. Для расширения области действия обычно следует использовать ключевое слово BEGIN. В противном случае возможен вызов функции до инициализации переменной.
BEGIN {
my @counter = 42;
sub riext_couhter {return ++$counter }
sub prev_counter { return --$counter } } .
Таким образом, в Perl создается аналог статических переменных языка С. В действительности он даже лучше - переменная не ограничивается одной функцией, и обе функции могут совместно использовать свою закрытую переменную.
> Смотри также ------------------------------
Раздел "Private Variables via my()" perlsub(1); раздел "Package Constructors and ; Destructors" perlmod(1); рецепт 11.4.