Получение ссылок на функции
Проблема
Требуется создать ссылку для вызова подпрограммы. Такая задача возникает при создании
обработчиков сигналов, косвенно-вызываемых функций Tk и указателей на хэши функций.
Решение
Получение ссылки на функцию:
$cref = \&func;
$cref = sub {...};
Вызов функции по ссылке:
@returned = $cref->(@arguments);
@returned = &$cref (@arguments);
Комментарий
Чтобы получить ссылку на функцию, достаточно снабдить ее имя префиксом \&. Кроме того,
формулировка sub {} позволяет создавать анонимные функции. Ссылка на анонимную функцию может
быть сохранена так же, как и любая другая.
В Perl 5.004 появилась постфиксная запись для разыменования ссылок на функции. Чтобы
вызвать функцию по ссылке, раньше приходилось писать &$funcname (@ARGS), где $funcname —
имя функции. Возможность сохранить имя функции в переменной осталась и сейчас:
$funcname = "thefunc";
&$funcname();
однако подобное решение нежелательно по нескольким причинам. Во-первых, в нем используются
символические, а не настоящие (жесткие) ссылки, поэтому при действующей директиве
use strict 'refs' оно отпадает. Символические ссылки обычно не рекомендуются, поскольку
они не могут обращаться к лексическим, а только к глобальным переменным, и для них не
ведется подсчет ссылок.
Во-вторых, оно не содержит данных о пакете, поэтому выполнение фрагмента в другом
пакете может привести к вызову неверной функции. Наконец, если функция была в какой-то
момент переопределена (хотя это происходит нечасто), символическая ссылка будет обращаться
к текущему определению функции, а жесткая ссылка сохранит старое определение.
Вместо того чтобы сохранять имя функции в переменной, создайте ссылку на нее с
помощью оператора \. Именно так следует сохранять функцию в переменной или передавать
ее другой функции. Ссылки на именованные функции можно комбинировать со ссылками на анонимные функции:
my %commands = (
"happy" => \&joy,
"sad" => \&sullen,
"done" => sub { die "See ya!" },
"mad" => \&angry,
);
print "How are you?";
chomp($string = <STDIN>);
if ($commands{$string}) {
$commands{$string}->();
} else {
print "No such command: $string\n";
}
Если вы создаете анонимную функцию, которая ссылается на лексическую (mу) переменную
из вмещающей области действия, схема подсчета ссылок гарантирует, что память лексической
переменной не будет освобождена при наличии ссылок на нее:
sub counter_maker {
my $start = 0;
return sub { # Замыкание
return $start++; # Лексическая переменная
}; # из вмещающей области действия
}
$counter = counter_maker();
for ($i =0; $i < 5; $i ++) {
print &$counter, "\n";
}
Даже несмотря на то что функция counter_maker завершилась, а переменная $start
вышла из области действия, Perl не освобождает ее, поскольку анонимная подпрограмма
(на которую ссылается $counter) все еще содержит ссылку на $start. Если повторно
вызвать counter_maker, функция вернет ссылку на другую анонимную подпрограмму,
использующую другое значение $start:
$counter1 = counter_maker();
$counter2 = counter_maker();
for ($i = 0; $i < 5; $i ++) {
print &$counter1, "\n";
}
print &$counter1, " ", &$counter2, "\n";
012345 0
Замыкания часто используются в косвенно-вызываемых функциях (callbacks). В графических
интерфейсах и вообще в программировании, основанном на событиях, определенные фрагменты
кода связываются с событиями нажатий клавиш, щелчков мышью, отображения окон и т. д.
Этот код вызывается много позже, возможно, из совсем другой области действия. Переменные,
используемые в замыкании, должны быть доступными к моменту вызова. Для нормальной работы
они должны быть лексическими, а не глобальными.
Замыкания также используются в генераторах функций, то есть в функциях, которые
создают и возвращают другие функции. Функция counter_maker является генератором. Приведем
еще один простой пример:
sub timestamp {
my $start_time = time();
return sub { return time() - $start_time };
}
$early = timestamp();
sleep 20;
$later = timestamp();
sleep 10;
printf "It's been %d seconds since early.\n", $early->();
printf "It's been %d seconds since later.\n", $later->();
It's been 30 seconds since early.
It's been 10 seconds since later.
Каждый вызов timestamp генерирует и возвращает новую функцию. Функция timestamp
создает лексическую переменную $start_time, которая содержит текущее время (в секундах
с начала эпохи). При каждом вызове замыкания оно возвращает количество прошедших секунд,
которое определяется вычитанием начального времени из текущего.
См. также
Proverte kod v komentariyah gde pro list tam oshibki detskie
Оставить комментарий:
|
|