Подтвердить что ты не робот

В Perl, как может подпрограмма получить coderef, который указывает на себя?

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

my $cb;
my $try = 3;
$cb = sub {
    my $rc = do_stuff();
    if (!$rc && --$try) {
        schedule_event($cb, 10); # schedule $cb to be called in 10 seconds
    } else {
        do_other_stuff;
    }
};
schedule_event($cb, 0); # schedule initial call to $cb to be performed ASAP

Есть ли способ, чтобы код внутри суб-канала мог получить доступ к coderef к этому sub, чтобы я мог обойтись без использования дополнительной переменной? Я бы хотел назначьте начальный вызов следующим образом.

schedule_event( sub { ... }, 0);

Сначала я подумал об использовании caller(0)[3], но это только дает мне имя функции, (__ANON__ если нет имени), а не ссылку на код к которому прикреплен блокнот.

4b9b3361

Ответ 1

Я думаю, Sub::Current устранит вашу проблему.

Ответ 2

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

use Scalar::Util qw/weaken/;

sub Y (&) {
    my ($code, $self, $return) = shift;
    $return = $self = sub {$code->($self, @_)};
    weaken $self;  # prevent a circular reference that will leak memory
    $return;
}

schedule_event( Y { my $self = shift; ... }, 0);

Ответ 3

__SUB__ добавлено в 5.16, обеспечивая это удобство использования.

Ответ 4

Если вы не измените значение $cb еще раз, вы можете использовать это. Если нет, определите скаляр, чтобы его удерживать, и не изменяйте его снова. Например:

my $cb = do {
  my $sub;
  $sub = sub { contents using $sub here }
}

Ответ 5

Используя комбинатор с фиксированной запятой, вы можете написать функцию $cb, как если бы первым аргументом была сама функция:

sub U {
  my $f = shift;
  sub { $f->($f, @_) }
}

my $cb = sub {
  my $cb = shift;
  ...
  schedule_event(U($cb), 10);
  ...
}

schedule_event(U($cb), 0);