В Perl, как подпрограмма может получить кодовую ссылку, которая указывает на себя? - PullRequest
26 голосов
/ 31 марта 2010

В учебных целях я играю с идеей построения управляемые событиями программы на 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 к этому саб, чтобы я мог обойтись без использования дополнительной переменной? Я бы хотел запланировать начальный звонок, как это.

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

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

Ответы [ 5 ]

14 голосов
/ 11 июня 2013

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

14 голосов
/ 31 марта 2010

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

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);
14 голосов
/ 31 марта 2010

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

5 голосов
/ 31 марта 2010

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

my $cb = do {
  my $sub;
  $sub = sub { contents using $sub here }
}
4 голосов
/ 01 апреля 2010

Используя комбинатор с фиксированной запятой, вы можете написать свою функцию $ 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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...